-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* docs: 오목 기능 목록 작성 * feat: 1부터 15 위치를 가진 오목알 클래스 구현 * feat: 중복 오목알 처리를 위한 오목알 일급 컬렉션 구현 * docs: .gitkeep 파일 제거 * feat: 오목알을 놓은 플레이어가 게임에서 이겼는지 확인하는 기능 구현 * feat: 사용자가 오목알을 놓았을 때 상태를 반환하는 기능 구현 * feat: 오목판에 돌을 올려놓는 기능 구현 * feat: 플레이어의 오목알을 놓는 턴을 바꾸는 기능 구현 * refactor: 돌을 놓으면 새로운 상태를 가진 플레이어 반환하는 기능 구현 * feat: 플레이어가 놓은 마지막 돌을 리턴하는 기능 구현 * refactor: 오목알을 놓을 수 있는지 확인하는 함수를 Players 클래스로 이동 * feat: 오목 게임 진행하는 기능 구현 * feat: 오목 게임 입력, 출력 화면 구현 * feat: 오목 컨트롤러 구현 * refactor: 도메인 패키지 분리 * fix: 중간에 오목알을 뒀을 때 승리 판정하지 않는 오류 수정 * feat: 렌주룰 기능 추가 (미완성) * feat: 렌주룰 적용 (완성) --------- Co-authored-by: tmdgh1592 <[email protected]>
- Loading branch information
Showing
38 changed files
with
1,071 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
## 오목 | ||
|
||
### Domain | ||
- [ ] 한 플레이어라도 승리할 때까지 차례를 번갈아가면서 돌을 놓는다. | ||
- [x] 흑, 백 플레이어를 가지고 있다 | ||
- [x] 흑돌이 먼저 시작한다. | ||
- [ ] 게임의 진행 여부는 `PlayerState`가 결정한다. | ||
- [x] 오목알은 자신의 위치를 알고 있다. | ||
- [x] `x, y` 위치는 `1부터 15`로 제한된다. | ||
- [x] 중복되는 위치의 오목알을 가질 수 없다. | ||
- [x] 오목판의 크기는 `15 x 15`이다. | ||
- [x] 사용자는 오목알을 놓는다. | ||
- [x] 오목알을 놓았을 때 5개 이상 연이어 있으면 승리한다. | ||
- [x] 오목알을 놓았을 때 5개 미만 연이어 있으면 게임을 계속 진행한다. | ||
- [x] 특정 위치에 돌을 놓을 수 있는지 판단한다. | ||
- [x] 플레이어는 흑과 백으로 이루어져 있다. | ||
- [x] 오목알을 놓은 플레이어가 게임에서 이겼는지 확인한다. | ||
- [x] 사용자는 특정 위치에 내 돌이 있는지 확인한다. | ||
- [x] 사용자는 마지막 돌의 위치를 알고 있다. | ||
- [x] 오목알을 놓으면 상대방의 차례가 된다. | ||
|
||
### Input | ||
- [ ] 오목알을 놓을 위치를 입력받는다. | ||
|
||
### Output | ||
- [ ] 오목알의 위치를 입력받기 전에 오목판을 출력한다. | ||
- [ ] 마지막 돌의 위치를 출력한다. |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import controller.OmokController | ||
|
||
fun main() { | ||
OmokController().start() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package controller | ||
|
||
import domain.game.Omok | ||
import domain.rule.RenjuRule | ||
import view.InputView | ||
import view.OutputView | ||
|
||
class OmokController { | ||
fun start() { | ||
Omok(OutputView(), InputView(), RenjuRule()).run() | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package domain.board | ||
|
||
import domain.player.Player | ||
import domain.player.Players | ||
import domain.rule.OmokRule | ||
import domain.stone.Stone | ||
import domain.stone.StoneColor | ||
|
||
class Board(private val players: Players) { | ||
constructor(blackPlayer: Player, whitePlayer: Player, rule: OmokRule) : this(Players(blackPlayer, whitePlayer, rule)) | ||
|
||
fun putStone(stoneColor: StoneColor, stone: Stone): Board? { | ||
if (players.canPlace(stone)) { | ||
return Board(players.putStone(stoneColor, stone)) | ||
} | ||
return null | ||
} | ||
|
||
fun getPlayers(): Players = players.copy() | ||
|
||
fun isRunning(): Boolean = players.isRunning | ||
|
||
fun isLose(): Boolean = players.isBlackLose | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
package domain.game | ||
|
||
import domain.board.Board | ||
import domain.player.BlackPlayer | ||
import domain.player.WhitePlayer | ||
import domain.rule.OmokRule | ||
import domain.stone.Stone | ||
import domain.stone.StoneColor | ||
import listener.OmokStartEndEventListener | ||
import listener.OmokTurnEventListener | ||
|
||
class Omok( | ||
private val startEndEventListener: OmokStartEndEventListener, | ||
private val turnEventListener: OmokTurnEventListener, | ||
private val rule: OmokRule | ||
) { | ||
fun run() { | ||
startEndEventListener.onStartGame() | ||
var curStoneColor: StoneColor = StoneColor.BLACK | ||
var curBoard = Board(BlackPlayer(), WhitePlayer(), rule) | ||
do { | ||
curBoard = takeTurn(curBoard, curStoneColor) | ||
startEndEventListener.onEndTurn(curBoard.getPlayers()) | ||
curStoneColor = curStoneColor.next() | ||
} while (curBoard.isRunning()) | ||
if (curBoard.isLose()) startEndEventListener.onEndGame(curStoneColor) | ||
else startEndEventListener.onEndGame(curStoneColor.next()) | ||
} | ||
|
||
private fun takeTurn(board: Board, stoneColor: StoneColor): Board { | ||
val newStone = Stone.of(turnEventListener.onTakeTurn(stoneColor)) | ||
val newBoard = board.putStone(stoneColor, newStone) | ||
if (newBoard == null) { | ||
turnEventListener.onNotPlaceable() | ||
return takeTurn(board, stoneColor) | ||
} | ||
return newBoard | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package domain.player | ||
|
||
import domain.rule.OmokRule | ||
import domain.state.LoseState | ||
import domain.state.PlayerState | ||
import domain.state.PlayingState | ||
import domain.stone.Stone | ||
import domain.stone.Stones | ||
|
||
class BlackPlayer(state: PlayerState = PlayingState()) : Player(state) { | ||
val isLose | ||
get() = state is LoseState | ||
|
||
override fun putStone(stone: Stone, otherStones: Stones, rule: OmokRule): Player { | ||
val blackStones = state.getAllStones() | ||
if (rule.check(blackStones, otherStones, stone)) { | ||
return BlackPlayer(LoseState(state.getAllStones())) | ||
} | ||
return BlackPlayer(state.add(stone, rule)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package domain.player | ||
|
||
import domain.position.Position | ||
import domain.rule.OmokRule | ||
import domain.state.LoseState | ||
import domain.state.PlayerState | ||
import domain.state.WinState | ||
import domain.stone.Stone | ||
import domain.stone.Stones | ||
|
||
abstract class Player(protected val state: PlayerState) : Cloneable { | ||
fun canPlace(): Boolean = state !is WinState && state !is LoseState | ||
|
||
fun isPlaced(stone: Stone): Boolean = state.hasStone(stone) | ||
|
||
fun getPositions(): List<Position> = state.getPlaced() | ||
|
||
fun getLastStone(): Stone = state.getLastStone() | ||
|
||
fun getAllStones(): Stones = state.getAllStones() | ||
|
||
abstract fun putStone(stone: Stone, otherStones: Stones, rule: OmokRule): Player | ||
|
||
public override fun clone(): Player = super.clone() as Player | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package domain.player | ||
|
||
import domain.rule.OmokRule | ||
import domain.rule.RenjuRule | ||
import domain.stone.Stone | ||
import domain.stone.StoneColor | ||
|
||
data class Players private constructor(private val players: List<Player>, private val rule: OmokRule) { | ||
val isRunning: Boolean | ||
get() = players.all { it.canPlace() } | ||
val isBlackLose: Boolean | ||
get() = (getBlackPlayer() as BlackPlayer).isLose | ||
|
||
constructor(blackPlayer: Player, whitePlayer: Player, rule: OmokRule) : this( | ||
listOf( | ||
blackPlayer.clone(), | ||
whitePlayer.clone() | ||
), | ||
rule | ||
) | ||
|
||
fun putStone(stoneColor: StoneColor, stone: Stone): Players { | ||
val whiteStones = getWhitePlayer().getAllStones() | ||
val blackStones = getBlackPlayer().getAllStones() | ||
|
||
return when (stoneColor) { | ||
StoneColor.BLACK -> { | ||
Players( | ||
blackPlayer = getBlackPlayer().putStone(stone, whiteStones, RenjuRule()), | ||
whitePlayer = getWhitePlayer(), | ||
rule, | ||
) | ||
} | ||
|
||
StoneColor.WHITE -> { | ||
Players( | ||
blackPlayer = getBlackPlayer(), | ||
whitePlayer = getWhitePlayer().putStone(stone, blackStones, rule), | ||
rule, | ||
) | ||
} | ||
} | ||
} | ||
|
||
fun getBlackPlayer(): Player = players.first { it is BlackPlayer } | ||
|
||
fun getWhitePlayer(): Player = players.first { it is WhitePlayer } | ||
|
||
fun canPlace(stone: Stone): Boolean = players.none { it.isPlaced(stone) } | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package domain.player | ||
|
||
import domain.rule.OmokRule | ||
import domain.state.PlayerState | ||
import domain.state.PlayingState | ||
import domain.stone.Stone | ||
import domain.stone.Stones | ||
|
||
class WhitePlayer(state: PlayerState = PlayingState()) : Player(state) { | ||
override fun putStone(stone: Stone, otherStones: Stones, rule: OmokRule): Player = | ||
WhitePlayer(state.add(stone, rule)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package domain.position | ||
|
||
data class Position(val row: Int, val col: Int) { | ||
init { | ||
require(row in POSITION_RANGE) { ROW_OUT_OF_RANGE_ERROR_MESSAGE } | ||
require(col in POSITION_RANGE) { COLUMN_OUT_OF_RANGE_ERROR_MESSAGE } | ||
} | ||
|
||
companion object { | ||
private const val MIN_BOUND = 1 | ||
private const val MAX_BOUND = 15 | ||
val POSITION_RANGE = (MIN_BOUND..MAX_BOUND) | ||
private const val ROW_OUT_OF_RANGE_ERROR_MESSAGE = "행의 범위는 ${MIN_BOUND}부터 ${MAX_BOUND}입니다" | ||
private const val COLUMN_OUT_OF_RANGE_ERROR_MESSAGE = "열의 범위는 ${MIN_BOUND}부터 ${MAX_BOUND}입니다" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package domain.rule | ||
|
||
import domain.stone.Stone | ||
import domain.stone.Stones | ||
|
||
interface OmokRule { | ||
fun check(blackStones: Stones, whiteStones: Stones, startStone: Stone): Boolean | ||
} |
Oops, something went wrong.