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

[전남대 Android_장현지] 미션 제출합니다. #36

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
948bb17
docs: 기능 목록 작성
Hyeonz1 May 28, 2024
4f93b7c
feat: 검은돌과 흰돌을 번갈아가며 놓을 수 있는 기능 구현
Hyeonz1 May 29, 2024
102f1a8
update: Player 객체를 통해 stone과 resourceId에 접근할 수 있도록 수정
Hyeonz1 May 29, 2024
6df4845
docs: 기능 목록 세분화
Hyeonz1 Jun 7, 2024
c97db02
feat: 오목 승자 판정 기능 구현
Hyeonz1 Jun 7, 2024
7be06f1
feat: 승자 표시 및 게임 종료 기능 구현
Hyeonz1 Jun 7, 2024
badbe78
feat: 현재 turn과 player를 표시하는 기능 추가
Hyeonz1 Jun 7, 2024
6908421
style: MainActivity 코드 스타일 수정
Hyeonz1 Jun 7, 2024
ed4281b
refactor: OmokModel에서 OmokBoard 객체 추출
Hyeonz1 Jun 7, 2024
8ee69a2
refactor: sealed class 명 변경 - Player to PlayerType
Hyeonz1 Jun 7, 2024
d0be024
refactor: OmokModel에서 CurrentPlayer 객체 추출
Hyeonz1 Jun 7, 2024
8bf9203
refactor: OmokBoard를 클래스에서 오브젝트로 변경
Hyeonz1 Jun 7, 2024
a3cec69
style: 함수 순서 정리
Hyeonz1 Jun 7, 2024
c1db1fd
refactor: OmokPresenter 함수 세분화
Hyeonz1 Jun 7, 2024
9a19315
style: 코드 스타일 수정
Hyeonz1 Jun 7, 2024
2889087
refactor: GameStateValidator 클래스를 오브젝트로 변경
Hyeonz1 Jun 7, 2024
2f3ba2a
refactor: OmokModel 멤버 변수 winner 삭제
Hyeonz1 Jun 7, 2024
a05c3f5
refactor: 승자 판정 로직 수정
Hyeonz1 Jun 7, 2024
1d1e899
update: UI 정렬 조정
Hyeonz1 Jun 7, 2024
e0da51c
update: notice 텍스트 UI 세부 사항 조정 및 초기 텍스트 설정
Hyeonz1 Jun 7, 2024
6152a0d
update: 게임 종료 시 안내 문구 수정
Hyeonz1 Jun 7, 2024
c8e7297
update: PlayerTextAdapter 추가
Hyeonz1 Jun 7, 2024
c39b8f3
fix: 한 번 수를 놓은 곳에 다시 돌을 놓을 수 없도록 수정
Hyeonz1 Jun 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,12 @@
# android-omok-precourse
# android-omok-precourse

### 기능 목록
- 수는 검은돌, 흰돌을 번갈아가며 놓을 수 있다.
- 가로, 세로, 대각선에서 같은 색상의 돌이 5개 이상 놓이면, 해당 색상의 돌이 승리한다.
- 승자가 결정되면, 게임이 종료된다.

### 요구사항
- build.gradle.kts 파일을 변경하지 않는다.
- 테스트 코드를 사용하여 기능 목록이 정상적으로 작동하는지 테스트한다.
- 함수 라인이 15라인을 넘어가지 않도록 구현한다.
- 예외 처리를 통해 프로그램의 강제 종료를 방지한다.
2 changes: 1 addition & 1 deletion app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
android:theme="@style/Theme.Omok"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:name=".view.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand Down
23 changes: 0 additions & 23 deletions app/src/main/java/nextstep/omok/MainActivity.kt

This file was deleted.

37 changes: 37 additions & 0 deletions app/src/main/java/nextstep/omok/OmokContract.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package nextstep.omok

import nextstep.omok.model.GameState
import nextstep.omok.model.IntersectionState
import nextstep.omok.model.PlayerType

interface OmokContract {
interface OmokView {
fun showTurn(currentTurn: Int, currentPlayerType: PlayerType)
fun placeStone(rowIndex: Int, colIndex: Int, playerType: PlayerType)
fun endGame(winner: PlayerType)
}

interface OmokModel {
/* currentPlayer 관련 함수 */
fun togglePlayer()
fun getPlayer(): PlayerType

/* omokBoard 관련 함수 */
fun updateBoard(rowIndex: Int, colIndex: Int, stone: IntersectionState)
fun getBoardState(rowIndex: Int, colIndex: Int): IntersectionState

/* turn 관련 함수 */
fun addTurnCount()
fun getTurn(): Int

/* currentGameState 관련 함수 */
fun checkWinnerExist()
fun getGameState(): GameState
}

interface OmokPresenter {
fun onIntersectionClick(rowIndex: Int, colIndex: Int)
// Player에 맞는 돌을 놓는다.
// turn이 9이상일 때부터, 게임이 종료되었는지 판단한다.
}
}
16 changes: 16 additions & 0 deletions app/src/main/java/nextstep/omok/model/CurrentPlayer.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package nextstep.omok.model

object CurrentPlayer {
private var currentPlayer: PlayerType = PlayerType.WithBlackStone

fun togglePlayer() {
currentPlayer = when (currentPlayer) {
PlayerType.WithBlackStone -> PlayerType.WithWhiteStone
PlayerType.WithWhiteStone -> PlayerType.WithBlackStone
}
}

fun getPlayer(): PlayerType {
return currentPlayer
}
}
6 changes: 6 additions & 0 deletions app/src/main/java/nextstep/omok/model/GameState.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package nextstep.omok.model

sealed class GameState {
data object OnGoing : GameState()
data object End : GameState()
}
7 changes: 7 additions & 0 deletions app/src/main/java/nextstep/omok/model/IntersectionState.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package nextstep.omok.model

sealed class IntersectionState {
data object Empty: IntersectionState()
data object OnWhiteStone: IntersectionState()
data object OnBlackStone: IntersectionState()
}
27 changes: 27 additions & 0 deletions app/src/main/java/nextstep/omok/model/OmokBoard.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package nextstep.omok.model

object OmokBoard {
private val omokBoard: MutableList<MutableList<IntersectionState>> = mutableListOf()

init {
for(i in 0..14) {
val boardRow = mutableListOf<IntersectionState>()
for(j in 0..14) {
boardRow.add(IntersectionState.Empty)
}
omokBoard.add(boardRow)
}
}

fun updateBoard(rowIndex: Int, colIndex: Int, stone: IntersectionState) {
omokBoard[rowIndex][colIndex] = stone
}

fun getBoard(): List<List<IntersectionState>> {
return omokBoard
}

fun getBoardIntersection(row: Int, col: Int): IntersectionState {
return omokBoard[row][col]
}
}
57 changes: 57 additions & 0 deletions app/src/main/java/nextstep/omok/model/OmokModel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package nextstep.omok.model

import nextstep.omok.OmokContract
import nextstep.omok.util.GameStateValidator

class OmokModel : OmokContract.OmokModel {
private var currentPlayer: CurrentPlayer = CurrentPlayer
private val omokBoard = OmokBoard
private var turn: Int = 1
private var currentGameState: GameState = GameState.OnGoing
private val gameStateValidator: GameStateValidator by lazy { GameStateValidator }

/* currentPlayer 관련 함수 */
override fun togglePlayer() {
currentPlayer.togglePlayer()
}

override fun getPlayer(): PlayerType {
return currentPlayer.getPlayer()
}


/* omokBoard 관련 함수 */
override fun updateBoard(rowIndex: Int, colIndex: Int, stone: IntersectionState) {
omokBoard.updateBoard(rowIndex, colIndex, stone)
}

override fun getBoardState(rowIndex: Int, colIndex: Int): IntersectionState {
return omokBoard.getBoardIntersection(rowIndex, colIndex)
}


/* turn 관련 함수 */
override fun addTurnCount() {
turn++
}

override fun getTurn(): Int {
return turn
}


/* currentGameState 관련 함수 */
override fun checkWinnerExist() {
if (gameStateValidator.isWinnerExist(omokBoard.getBoard())) {
endGame()
}
}

override fun getGameState(): GameState {
return currentGameState
}

private fun endGame() {
currentGameState = GameState.End
}
}
17 changes: 17 additions & 0 deletions app/src/main/java/nextstep/omok/model/PlayerType.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package nextstep.omok.model

import nextstep.omok.R

sealed class PlayerType {
abstract val stone: IntersectionState
abstract val resourceId: Int
data object WithWhiteStone: PlayerType() {
override val stone = IntersectionState.OnWhiteStone
override val resourceId: Int = R.drawable.white_stone
}

data object WithBlackStone: PlayerType() {
override val stone = IntersectionState.OnBlackStone
override val resourceId: Int = R.drawable.black_stone
}
}
52 changes: 52 additions & 0 deletions app/src/main/java/nextstep/omok/presenter/OmokPresenter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package nextstep.omok.presenter

import nextstep.omok.OmokContract
import nextstep.omok.model.GameState
import nextstep.omok.model.IntersectionState

class OmokPresenter(
private var activity: OmokContract.OmokView,
private var model: OmokContract.OmokModel
) : OmokContract.OmokPresenter {
override fun onIntersectionClick(rowIndex: Int, colIndex: Int) {
if (model.getBoardState(rowIndex, colIndex) == IntersectionState.Empty) {
updateBoard(rowIndex, colIndex)
checkWinnerExist()
}
}

private fun updateBoard(rowIndex: Int, colIndex: Int) {
model.updateBoard(rowIndex, colIndex, model.getPlayer().stone)
activity.placeStone(rowIndex, colIndex, model.getPlayer())
}

private fun checkWinnerExist() {
if (model.getTurn() > 8) {
model.checkWinnerExist()
checkCurrentGameState()
} else {
onGameOnGoing()
}
}

private fun checkCurrentGameState() {
when (model.getGameState()) {
GameState.OnGoing -> {
onGameOnGoing()
}
GameState.End -> {
onGameEnd()
}
}
}

private fun onGameOnGoing() {
model.togglePlayer()
model.addTurnCount()
activity.showTurn(model.getTurn(), model.getPlayer())
}

private fun onGameEnd() {
activity.endGame(model.getPlayer())
}
}
62 changes: 62 additions & 0 deletions app/src/main/java/nextstep/omok/util/GameStateValidator.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package nextstep.omok.util

import nextstep.omok.model.IntersectionState

private const val WIN_CONDITION = 5

object GameStateValidator {
private var board: List<List<IntersectionState>> = listOf()
private val directions = arrayOf(Pair(0, 1), Pair(1, 0), Pair(1, 1), Pair(1, -1),)

fun isWinnerExist(board: List<List<IntersectionState>>): Boolean {
this.board = board
for (row in board.indices) {
for (col in board[row].indices) {
if (checkBoard(col, row))
return true
}
}
return false
}

private fun checkBoard(col: Int, row: Int): Boolean {
val currentIntersection = board[row][col]
if (currentIntersection == IntersectionState.Empty)
return false

val isConditionSatisfied = checkAllDirections(col, row, currentIntersection)
if (!isConditionSatisfied)
return false

return when (currentIntersection) {
IntersectionState.OnBlackStone -> true
IntersectionState.OnWhiteStone -> true
IntersectionState.Empty -> false
}
}

private fun checkAllDirections(
col: Int, row: Int, currentIntersection: IntersectionState
): Boolean {
for (direction in directions) {
val (dRow, dCol) = direction
if (checkSingleDirection(col, row, dCol, dRow, currentIntersection)) {
return true
}
}
return false
}

private fun checkSingleDirection(
col: Int, row: Int, dCol: Int, dRow: Int, currentIntersection: IntersectionState
): Boolean {
for (i in 0 until WIN_CONDITION) {
val nx = col + i * dCol
val ny = row + i * dRow
if (nx !in board[0].indices || ny !in board.indices || board[ny][nx] != currentIntersection) {
return false
}
}
return true
}
}
13 changes: 13 additions & 0 deletions app/src/main/java/nextstep/omok/util/PlayerTextAdapter.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package nextstep.omok.util

import nextstep.omok.model.PlayerType

const val PLAYER_WITH_BLACK_STONE = "흑돌"
const val PLAYER_WITH_WHITE_STONE = "백돌"

fun adaptPlayerText(currentPlayerType: PlayerType): String {
return when (currentPlayerType) {
PlayerType.WithBlackStone -> PLAYER_WITH_BLACK_STONE
PlayerType.WithWhiteStone -> PLAYER_WITH_WHITE_STONE
}
}
Loading