Skip to content
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

[둘리] 1, 2단계 오목 제출합니다. #26

Merged
merged 18 commits into from
Mar 17, 2023

Conversation

hyemdooly
Copy link

@hyemdooly hyemdooly commented Mar 16, 2023

안녕하세요, 리뷰어님!
라이브러리를 사용하지 않고 직접 렌주룰을 작성하려고 노력했습니다.
처음으로 함수형 프로그래밍을 적용해본거라, 부족한 부분 날카롭게 피드백 주시면 감사하겠습니다. :)

Copy link

@laco-dev laco-dev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오목 미션 고생 많으셨습니다.

불변 객체를 유지하려는 모습이 보기 좋았습니다.
대신 그만큼 구현 복잡도가 올라가고 객체의 역할을 결정하기가 어려운 단점이 있습니다.

현재 돌, 플레이어, 플레이어들, 게임, 상태에서 하는 역할들이 다소 명확하게 정의되어있는 느낌이 듭니다.

관련해서 몇가지 피드백을 남겨드렸으니 충분히 고민 해보시고 도전 해보세요.

Comment on lines +3 to +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
- [ ] 오목알의 위치를 입력받기 전에 오목판을 출력한다.
- [ ] 마지막 돌의 위치를 출력한다.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

모든 규칙은 적용되었지만... 미완성인걸까요!?

Comment on lines +9 to +11
fun start() {
Omok(OutputView(), InputView(), RenjuRule()).run()
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오목 게임으로부터 어떤 규칙을 적용할지 넘겨주는 방식 좋습니다!
이로 인해 다른 규칙이 적용된 오목 게임을 코드수정없이 즐길 수 있겠네요.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

사용하지 않는 코드라면 지워주세요.

주석으로만 되어 있는 상태로 코드가 올라가면 왜 올라갔는지는 작성자밖에 알지 못합니다.

Comment on lines +19 to +25
if (input.length !in POSITION_INPUT_RANGE)
return reAskPosition(INVALID_FORMAT_ERROR_MESSAGE)

val col = BoardModel.getColInt(input.first().toString())
val row = input.substring(ROW_INPUT_SIZE).toIntOrNull()
if (row == null || row !in POSITION_RANGE || col !in POSITION_RANGE)
return reAskPosition(INVALID_FORMAT_ERROR_MESSAGE)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

InputView가 로직에 대한 처리를 너무 자세하게 알고 있지는 않을까요?
행과, 열에 해당하는 위치를 만드는 것은 Position 객체가 해야할 책임으로 볼 수 있지 않을까요?

논리적인 오류가 아니라면 null을 반환하도록 만들어 볼 수 있겠습니다.

https://medium.com/@galcyurio/kotlin%EC%97%90%EC%84%9C%EC%9D%98-%EC%98%88%EC%99%B8-%EC%B2%98%EB%A6%AC-%EB%B0%A9%EB%B2%95-48a5cd94a4e6

Comment on lines +9 to +15
fun `오목알의 위치는 범위가 1부터 15인 x, y를 가지고 있다`() {
val position = Position(10, 10)
assertAll({
assertThat(position.row).isEqualTo(10)
assertThat(position.col).isEqualTo(10)
})
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오목 게임의 규칙을 외부에서 결정하는 것처럼, 보드판의 크기도 외부에서 결정을 할 수 있다고 볼 수 있지 않을까요?

가령, 20x20 혹은 10x10 크기의 보드판에서 게임을 해야한다면 위 테스트는 터집니다 💣

Comment on lines +12 to +17
fun putStone(stoneColor: StoneColor, stone: Stone): Board? {
if (players.canPlace(stone)) {
return Board(players.putStone(stoneColor, stone))
}
return null
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

돌을 배치할 수 없는 경우, null을 반환하는 것이 논리적인 흐름에 맞을까요?

왜 null이지 라는 의문을 가질 수 있지는 않을까요?
실패했는지? 놓을 수 없는지? 버그가 있는지? 보드가 못생겼는지?

아무런 변경이 없다면, 자기 자신을 반환하도록 할 수 있지 않을까요?

Comment on lines +22 to +43
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,
)
}
}
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

플레이어들을 Player 타입으로 일반화 했지만 실제 구현은 블랙, 화이트에 대한 강한 의존성을 가지고 있습니다.

제가 생각한 플레이어들의 개념은 현재 턴의 플레이어가 해당 위치에 돌을 착수한다 라고 판단했습니다.

마지막에 착수한 플레이어를 Players에서 관리하도록 하면 어떨까요?

private var latestPlayer: Player?

fun putStone(...) {
   val othersStones = latestPlayer?.getAllStones()
   val currentPlayer = nextPlayers()
   latestPlayer = currentPlayer
   ...
}

불변 객체를 유지하려면, 살짝의 구현방법을 변경하면 간단하게 수정할 수 있습니다.

Comment on lines +8 to +12
override fun add(newStone: Stone, rule: OmokRule): PlayerState {
val newStones = stones.add(newStone)
if (newStones.checkWin(newStone)) return WinState(newStones)
return PlayingState(newStones)
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

새로운 돌을 두기 위해서는 오목 룰을 사용해야 하지 않을까요?

돌을 둔다
돌을 둘 수 있는가?
돌을 둘 수 있는지 확인한다.
반칙인가?
승리했는가?
...

Comment on lines +7 to +9
class LoseState(stones: Stones) : PlayerState(stones) {
override fun add(newStone: Stone, rule: OmokRule): PlayerState = this
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lose와 Win의 공통 로직을 FinishedState로 만들어 볼 수 있지 않을까요?

Comment on lines +21 to +25
do {
curBoard = takeTurn(curBoard, curStoneColor)
startEndEventListener.onEndTurn(curBoard.getPlayers())
curStoneColor = curStoneColor.next()
} while (curBoard.isRunning())

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

가급적 do, while은 사용을 지양하는면 좋습니다.
while로 표현할 수는 없을까? 를 먼저 생각 해보세요.

조건을 마지막에 확인하기 때문에 코드를 읽는데 어려움이 있고 가독성을 낮춥니다.

var isRunning = curBoard.isRunning()
while (isRunning) {
   ...
   isRunning = curBoard.isRunning()
}

Copy link

@laco-dev laco-dev left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리뷰 규칙 (6시 이후의 플랫폼 요청은 머지)에 따라 머지하도록 하겠습니다.

피드백 없이 하는 것이 원칙으로 되어 있고
아직 다음 단계가 남아있기 때문에 3, 4단계 미션에서 새롭게 리뷰를 진행하도록 하겠습니다. 🙏🏻

@laco-dev laco-dev merged commit 5ccb0b3 into woowacourse:hyemdooly Mar 17, 2023
hyemdooly added a commit to hyemdooly/kotlin-omok that referenced this pull request Mar 24, 2023
* 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]>
hyemdooly added a commit to hyemdooly/kotlin-omok that referenced this pull request Mar 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants