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

[부나] 3, 4단계 오목 제출합니다. #32

Merged
merged 29 commits into from
Mar 27, 2023
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
8d59987
feat: set up the android project
woowahan-pjs Mar 14, 2023
fae3223
feat: add main activity and resources
woowahan-pjs Mar 21, 2023
ea6854b
feat: 안드로이드 프로젝트 적용
tmdgh1592 Mar 22, 2023
2f4f911
feat: domain을 안드로이드로 이동
tmdgh1592 Mar 22, 2023
dad2995
feat(android): 오목 게임 구현
tmdgh1592 Mar 22, 2023
9f32c95
feat: 오목 데이터베이스 헬퍼 구현
tmdgh1592 Mar 23, 2023
f8c5492
chore: 불필요한 의존성 제거
tmdgh1592 Mar 23, 2023
84e3150
refactor: 데이터베이스 모듈화
tmdgh1592 Mar 23, 2023
169c6c2
feat: 결과 출력 화면 구현
tmdgh1592 Mar 23, 2023
f4153d7
feat: 오목판 색상 변경
tmdgh1592 Mar 23, 2023
99a94dd
feat: 캐릭터 턴 홀더 이미지 적용
tmdgh1592 Mar 23, 2023
7ea3064
fix: 이전 게임을 불러오면 오목알을 놓은 결과를 모두 재처리하는 오류 수정
tmdgh1592 Mar 23, 2023
2990bb6
fix: 이전 게임을 불러 왔을 때 캐릭터 홀더 이미지는 변경되지 않는 버그 수정
tmdgh1592 Mar 23, 2023
f314d9d
feat: 불필요한 클래스 제거
tmdgh1592 Mar 24, 2023
78b7c98
refactor: manifest에 마지막 개행 추가
tmdgh1592 Mar 25, 2023
1713db9
refactor: 불필요한 클래스 제거
tmdgh1592 Mar 25, 2023
b66ca08
refactor: Repository를 Generic을 사용하여 일반화
tmdgh1592 Mar 25, 2023
5ca1a10
refactor: 레이아웃 속성의 left, right를 start, end로 변경
tmdgh1592 Mar 25, 2023
89fb85c
chore: Coroutine 라이브러리 추가
tmdgh1592 Mar 25, 2023
5f561a1
refactor: MainActivity에서 컨트롤러를 재사용하도록 변경
tmdgh1592 Mar 26, 2023
add4939
refactor: presentation과 data 분리
tmdgh1592 Mar 26, 2023
d4e3c81
feat: intent의 Serializable 객체를 가져오는 확장 함수 구현
tmdgh1592 Mar 26, 2023
3cf09c5
refactor: 마지막 돌을 놓은 위치를 보여주는 화면 구현
tmdgh1592 Mar 26, 2023
c0355ae
chore: androidx.lifecycle:lifecycle-runtime-ktx:2.6.1 라이브러리 추가
tmdgh1592 Mar 27, 2023
4009199
refactor: MainActivity에서 CoroutineScope를 구현하지 않고 lifecycleScope를 사용하도…
tmdgh1592 Mar 27, 2023
c1d4f10
refactor: readPoint() 메서드를 suspendCoroutine을 사용하여 Point를 받도록 변경
tmdgh1592 Mar 27, 2023
8b8a053
refactor: 콘솔 관련 파일을 console 패키지로 이동
tmdgh1592 Mar 27, 2023
744e6cd
refactor: Android와 Console Controller 공통 로직 분리
tmdgh1592 Mar 27, 2023
6f5657c
refactor: 불필요한 클래스 제거
tmdgh1592 Mar 27, 2023
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
7 changes: 5 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@
android:theme="@style/Theme.Oomok"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:name=".activity.GameResultActivity"
android:exported="false" />
<activity
android:name=".activity.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
Expand All @@ -22,4 +25,4 @@
</activity>
</application>

</manifest>
</manifest>
galcyurio marked this conversation as resolved.
Show resolved Hide resolved
23 changes: 0 additions & 23 deletions app/src/main/java/woowacourse/omok/MainActivity.kt

This file was deleted.

64 changes: 64 additions & 0 deletions app/src/main/java/woowacourse/omok/activity/GameResultActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package woowacourse.omok.activity

import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.view.View
import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import woowacourse.omok.R
import woowacourse.omok.model.StoneColorModel
import woowacourse.omok.utils.setImageByResId

class GameResultActivity : AppCompatActivity(), View.OnClickListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_game_result)

setWinnerStoneImage()
setClickListener()
}

private fun setWinnerStoneImage() {
val winnerStoneColor = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
intent.getSerializableExtra("winner_color", StoneColorModel::class.java)
} else {
intent.getSerializableExtra("winner_color")
}
val winnerStoneIv = findViewById<ImageView>(R.id.winner_stone_iv)
val winnerStoneTv = findViewById<TextView>(R.id.winner_stone_tv)

when (winnerStoneColor) {
StoneColorModel.BLACK -> {
winnerStoneIv.setImageByResId(R.drawable.man_player)
winnerStoneTv.text = getString(R.string.winner_result_format, StoneColorModel.BLACK.text)
}
StoneColorModel.WHITE -> {
winnerStoneIv.setImageByResId(R.drawable.woman_player)
winnerStoneTv.text = getString(R.string.winner_result_format, StoneColorModel.WHITE.text)
}
}
}

private fun setClickListener() {
findViewById<View>(R.id.restart_btn).setOnClickListener(this)
findViewById<View>(R.id.end_game_btn).setOnClickListener(this)
}

override fun onClick(view: View) {
when (view.id) {
R.id.restart_btn -> restartGame()
R.id.end_game_btn -> endGame()
}
}

private fun restartGame() {
startActivity(Intent(this, MainActivity::class.java))
finish()
}

private fun endGame() {
finish()
}
}
158 changes: 158 additions & 0 deletions app/src/main/java/woowacourse/omok/activity/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package woowacourse.omok.activity

import android.content.ContentValues
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.ImageView
import android.widget.TableLayout
import android.widget.TableRow
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.children
import domain.game.*
import domain.point.Point
import domain.rule.BlackRenjuRule
import domain.rule.WhiteRenjuRule
import domain.stone.StoneColor
import woowacourse.omok.R
import woowacourse.omok.database.OmokDatabase
import woowacourse.omok.database.repository.OmokRepository
import woowacourse.omok.database.repository.Repository
import woowacourse.omok.mapper.toPresentation
import woowacourse.omok.model.StoneColorModel
import woowacourse.omok.utils.createAlertDialog
import woowacourse.omok.utils.message
import woowacourse.omok.utils.negativeButton
import woowacourse.omok.utils.positiveButton

class MainActivity : AppCompatActivity() {
private lateinit var board: TableLayout
private lateinit var omok: Omok
private lateinit var omokRepo: Repository
private lateinit var manTurnHolder: View
private lateinit var womanTurnHolder: View
galcyurio marked this conversation as resolved.
Show resolved Hide resolved

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

omokRepo = OmokRepository(OmokDatabase.getInstance(this))
omok = Omok(BlackRenjuRule(), WhiteRenjuRule())
initView()

if (hasPreviousGameHistory()) {
showRestartDialog()
}
setOmokClickListener()
showMessage(getString(R.string.omok_start_message))
}

private fun initView() {
board = findViewById(R.id.board)
manTurnHolder = findViewById(R.id.man_turn_iv)
womanTurnHolder = findViewById(R.id.woman_turn_iv)
}

private fun continuePreviousGame() {
omokRepo.getAll {
if (it.count % 2 == 1) toggleTurnHolder(StoneColorModel.BLACK)
while (it.moveToNext()) {
val col = it.getInt(it.getColumnIndexOrThrow("x"))
val row = it.getInt(it.getColumnIndexOrThrow("y"))
val index = row * Omok.OMOK_BOARD_SIZE + col
val omokIv: ImageView = boardImageViews()[index]
val putResult = omok.put { Point(row, col) }
if (putResult is PutSuccess) {
drawStoneOnBoard(omokIv, putResult.stoneColor)
}
}
}
}

Choose a reason for hiding this comment

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

UI layer에 ui, domain, data 코드가 모두 섞인 스파게티 코드입니다.
아래와 같은 순서로 개선해보세요.

  1. MainActivity에서 InputView와 OutputView를 구현하도록 만들어보세요.
  2. OmokController에서 OmokInputView, OmokOutputView가 아닌 InputView, OutputView interface에 의존하도록 만들어보세요.
  3. MainActivity에서 Controller를 사용하도록 만드세요.


private fun setOmokClickListener() {
boardImageViews().forEachIndexed { index, view ->
val row: Int = index / Omok.OMOK_BOARD_SIZE
val col: Int = index % Omok.OMOK_BOARD_SIZE

view.setOnClickListener {
val putResult = omok.put { Point(row, col) }
processPutResult(putResult, view)
}
}
}

private fun processPutResult(result: PutResult, view: ImageView) {
when (result) {
is PutSuccess -> {
toggleTurnHolder(result.stoneColor.toPresentation())
savePoint(result.stoneColor.toPresentation(), result.point)
drawStoneOnBoard(view, result.stoneColor)
}
is PutFailed -> showMessage(getString(R.string.inplace_stone))
is GameFinish -> {
omokRepo.clear()
showWinner(result.winnerStoneColor.toPresentation())
}
}
}

private fun toggleTurnHolder(prevStoneColor: StoneColorModel) {
when(prevStoneColor) {
StoneColorModel.BLACK -> {
manTurnHolder.visibility = View.GONE
womanTurnHolder.visibility = View.VISIBLE
}
StoneColorModel.WHITE -> {
womanTurnHolder.visibility = View.GONE
manTurnHolder.visibility = View.VISIBLE
}
}
}

private fun savePoint(stoneColor: StoneColorModel, point: Point) {
val record = ContentValues().apply {
put("stone_color", stoneColor.value)
put("x", point.col)
put("y", point.row)
}
omokRepo.insert(record)
}

private fun hasPreviousGameHistory(): Boolean = !omokRepo.isEmpty()

private fun drawStoneOnBoard(view: ImageView, stoneColor: StoneColor) {
when (stoneColor) {
StoneColor.BLACK -> view.setImageResource(R.drawable.pink_bear)
StoneColor.WHITE -> view.setImageResource(R.drawable.white_bear)
}
}

private fun showWinner(winnerColor: StoneColorModel) {
val resultIntent = Intent(this, GameResultActivity::class.java)
.putExtra("winner_color", winnerColor)
startActivity(resultIntent)
finish()
}

private fun showMessage(message: String) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
}

private fun showRestartDialog(): Unit = createAlertDialog {
message(getString(R.string.restart_game_ask_message))
positiveButton(getString(R.string.yes)) { continuePreviousGame() }
negativeButton(getString(R.string.no)) { omokRepo.clear() }
}.show()

private fun boardImageViews(): List<ImageView> = board.children
.filterIsInstance<TableRow>()
.flatMap { it.children }
.filterIsInstance<ImageView>()
.toList()

override fun onDestroy() {
super.onDestroy()
omokRepo.close()
}
}
19 changes: 19 additions & 0 deletions app/src/main/java/woowacourse/omok/database/OmokDBHelper.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package woowacourse.omok.database

import android.content.Context
import android.database.sqlite.SQLiteDatabase
import android.database.sqlite.SQLiteOpenHelper
import woowacourse.omok.database.contract.CREATE_POINTS_TABLE_QUERY
import woowacourse.omok.database.contract.DB_NAME
import woowacourse.omok.database.contract.DB_VERSION
import woowacourse.omok.database.contract.DROP_POINTS_TABLE_QUERY

class OmokDBHelper(context: Context) : SQLiteOpenHelper(context, DB_NAME, null, DB_VERSION) {
override fun onCreate(db: SQLiteDatabase?) {
db?.execSQL(CREATE_POINTS_TABLE_QUERY)
}

override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
db?.execSQL(DROP_POINTS_TABLE_QUERY)
}
}
9 changes: 9 additions & 0 deletions app/src/main/java/woowacourse/omok/database/OmokDatabase.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package woowacourse.omok.database

import android.content.Context
import android.database.sqlite.SQLiteDatabase

object OmokDatabase {
fun getInstance(context: Context): SQLiteDatabase =
OmokDBHelper(context).writableDatabase
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package woowacourse.omok.database.contract

internal const val DB_NAME: String = "omok.db"
internal const val DB_VERSION: Int = 4

const val POINT_TABLE_NAME: String = "points"
internal const val CREATE_POINTS_TABLE_QUERY = "CREATE TABLE $POINT_TABLE_NAME (" +
"id INTEGER PRIMARY KEY AUTOINCREMENT," +
"stone_color INTEGER NOT NULL," +
"x INTEGER NOT NULL," +
"y INTEGER NOT NULL" +
");"
internal const val DROP_POINTS_TABLE_QUERY = "DROP TABLE IF EXISTS $POINT_TABLE_NAME"
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package woowacourse.omok.database.repository

import android.content.ContentValues
import android.database.Cursor
import android.database.sqlite.SQLiteDatabase
import woowacourse.omok.database.contract.POINT_TABLE_NAME

class OmokRepository(private val database: SQLiteDatabase) : Repository {
override fun getAll(action: (Cursor) -> Unit) {
database.rawQuery("SELECT * FROM $POINT_TABLE_NAME", null).use { action(it) }
}
galcyurio marked this conversation as resolved.
Show resolved Hide resolved

override fun insert(record: ContentValues) {
database.insert(POINT_TABLE_NAME, null, record)
}
galcyurio marked this conversation as resolved.
Show resolved Hide resolved

override fun isEmpty(): Boolean =
database.rawQuery("SELECT * FROM $POINT_TABLE_NAME", null).use {
it.count == 0
}

override fun clear() {
database.execSQL("DELETE FROM $POINT_TABLE_NAME")
}

override fun close() {
database.close()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package woowacourse.omok.database.repository

import android.content.ContentValues
import android.database.Cursor

interface Repository {
fun getAll(action: (Cursor) -> Unit)
fun insert(record: ContentValues)
fun isEmpty(): Boolean
fun clear()
fun close()
}
14 changes: 14 additions & 0 deletions app/src/main/java/woowacourse/omok/mapper/StoneColorMapper.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package woowacourse.omok.mapper

import domain.stone.StoneColor
import woowacourse.omok.model.StoneColorModel

fun StoneColor.toPresentation(): StoneColorModel = when (this) {
StoneColor.BLACK -> StoneColorModel.BLACK
StoneColor.WHITE -> StoneColorModel.WHITE
}

fun StoneColorModel.toDomain(): StoneColor = when (this) {
StoneColorModel.BLACK -> StoneColor.BLACK
StoneColorModel.WHITE -> StoneColor.WHITE
}
8 changes: 8 additions & 0 deletions app/src/main/java/woowacourse/omok/model/StoneColorModel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package woowacourse.omok.model

import java.io.Serializable

enum class StoneColorModel(val text: String, val value: Int) : Serializable {
BLACK("흑", 0),
WHITE("백", 1),
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package woowacourse.omok.data.repository

class OmokRepository
galcyurio marked this conversation as resolved.
Show resolved Hide resolved
Loading