Added winner player checks
This commit is contained in:
parent
19df47972a
commit
db61c25b03
|
|
@ -0,0 +1,57 @@
|
||||||
|
package es.genol.tictactoe
|
||||||
|
|
||||||
|
import es.genol.tictactoe.data.model.Ficha
|
||||||
|
|
||||||
|
class GameChecks(private val player: Boolean, private val gridData: List<Ficha>) {
|
||||||
|
fun playerWinnerCheck(): Boolean {
|
||||||
|
if (diagonalCheck() || verticalCheck() || horizontalCheck()) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun diagonalCheck(): Boolean {
|
||||||
|
var result: Int
|
||||||
|
for (start in 0..2 step 2) {
|
||||||
|
var stage = 0
|
||||||
|
when (start){
|
||||||
|
0 -> stage = 4
|
||||||
|
2 -> stage = 2
|
||||||
|
}
|
||||||
|
result = 0
|
||||||
|
for (gridId in start..(stage * 2 + start) step stage) {
|
||||||
|
if (gridData[gridId].player == player) {
|
||||||
|
result++
|
||||||
|
}
|
||||||
|
if (result == 3) return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun verticalCheck(): Boolean {
|
||||||
|
var result: Int
|
||||||
|
for (start in 0..2) {
|
||||||
|
result = 0
|
||||||
|
for (gridId in start..start + 6 step 3) {
|
||||||
|
if (gridData[gridId].player == player) {
|
||||||
|
result++
|
||||||
|
}
|
||||||
|
if (result == 3) return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun horizontalCheck(): Boolean {
|
||||||
|
var result: Int
|
||||||
|
for (start in 0..6 step 3) {
|
||||||
|
result = 0
|
||||||
|
for (gridId in start..start + 2) {
|
||||||
|
if (gridData[gridId].player == player) {
|
||||||
|
result++
|
||||||
|
}
|
||||||
|
if (result == 3) return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
package es.genol.tictactoe.data.model
|
package es.genol.tictactoe.data.model
|
||||||
|
|
||||||
data class Ficha(val row: Int, val col: Int, var player: Boolean?)
|
data class Ficha(var player: Boolean? = null)
|
||||||
|
|
@ -1,57 +1,30 @@
|
||||||
package es.genol.tictactoe.ui.elements
|
package es.genol.tictactoe.ui.elements
|
||||||
|
|
||||||
import android.content.res.Configuration
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.material3.SnackbarDuration
|
|
||||||
import androidx.compose.material3.SnackbarHost
|
|
||||||
import androidx.compose.material3.SnackbarHostState
|
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBar
|
import androidx.compose.material3.TopAppBar
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalConfiguration
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import es.genol.tictactoe.ui.state.TicTacToeViewModel
|
import es.genol.tictactoe.ui.state.GameState
|
||||||
import es.genol.tictactoe.ui.theme.TicTacToeTheme
|
import es.genol.tictactoe.ui.theme.TicTacToeTheme
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun AppContent() {
|
fun AppContent() {
|
||||||
val viewModel: TicTacToeViewModel = viewModel()
|
val viewModel: GameState = viewModel()
|
||||||
val configuration = LocalConfiguration.current
|
|
||||||
val orientationHeight: Float
|
|
||||||
val orientationWidth: Float
|
|
||||||
val snackBarState = remember { SnackbarHostState() }
|
|
||||||
|
|
||||||
when (configuration.orientation) {
|
|
||||||
Configuration.ORIENTATION_PORTRAIT -> {
|
|
||||||
orientationHeight = .42f
|
|
||||||
orientationWidth = .65f
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
|
||||||
orientationHeight = 1f
|
|
||||||
orientationWidth = .35f
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (viewModel.isWinner) {
|
if (viewModel.isWinner) {
|
||||||
LaunchedEffect(key1 = viewModel.isWinner) {
|
|
||||||
viewModel.resetFromSnackBar(snackBarState.showSnackbar(
|
|
||||||
message = "Ganador",
|
|
||||||
actionLabel = "Reiniciar",
|
|
||||||
duration = SnackbarDuration.Indefinite
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TicTacToeTheme {
|
TicTacToeTheme {
|
||||||
|
|
@ -59,26 +32,32 @@ fun AppContent() {
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
color = MaterialTheme.colorScheme.background
|
color = MaterialTheme.colorScheme.background
|
||||||
) {
|
) {
|
||||||
Scaffold(topBar = {
|
Scaffold(
|
||||||
|
topBar = {
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = { Text(text = "TicTacToe") },
|
title = { Text(text = "TicTacToe") },
|
||||||
actions = {
|
actions = {
|
||||||
Button(onClick = { viewModel.boardReboot() }) {
|
Button(onClick = { viewModel.gridClean() }) {
|
||||||
Text(text = "REINICIAR")
|
Text(text = "REINICIAR")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}, snackbarHost = { SnackbarHost(hostState = snackBarState) }) {
|
},
|
||||||
Column(Modifier.padding(it)) {
|
) {
|
||||||
|
Column(
|
||||||
|
Modifier
|
||||||
|
.padding(it)
|
||||||
|
.padding(vertical = 15.dp)
|
||||||
|
.fillMaxWidth(),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
|
) {println("Antes de gameboard")
|
||||||
GameBoard(
|
GameBoard(
|
||||||
boardSize = 3,
|
boardSize = 3,
|
||||||
orientationHeight = orientationHeight,
|
playerValue = { gridId ->
|
||||||
orientationWidth = orientationWidth,
|
viewModel.gridState[gridId].player
|
||||||
playerValue = { row, col ->
|
|
||||||
viewModel.getPlayer(row, col)
|
|
||||||
},
|
},
|
||||||
buttonEnabled = !viewModel.isWinner
|
buttonEnabled = !viewModel.isWinner
|
||||||
) { row, col ->
|
) { gridId ->
|
||||||
viewModel.printPosition(row, col)
|
viewModel.gridMarkPlayer(gridId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,10 @@ package es.genol.tictactoe.ui.elements
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.aspectRatio
|
||||||
import androidx.compose.foundation.layout.fillMaxHeight
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
@ -17,38 +17,37 @@ import androidx.compose.ui.unit.dp
|
||||||
@Composable
|
@Composable
|
||||||
fun GameBoard(
|
fun GameBoard(
|
||||||
boardSize: Int,
|
boardSize: Int,
|
||||||
orientationHeight: Float,
|
playerValue: (Int) -> Boolean?,
|
||||||
orientationWidth: Float,
|
|
||||||
playerValue: (Int, Int) -> Boolean?,
|
|
||||||
buttonEnabled: Boolean,
|
buttonEnabled: Boolean,
|
||||||
onButtonClick: (Int, Int) -> Unit
|
onButtonClick: (Int) -> Unit
|
||||||
) {
|
) {
|
||||||
|
var gridId = 0
|
||||||
|
val grid = Array(boardSize) { IntArray(boardSize) { gridId++ } }
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
.width(240.dp)
|
||||||
.fillMaxHeight(orientationHeight),
|
.aspectRatio(1f),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
verticalArrangement = Arrangement.SpaceAround
|
verticalArrangement = Arrangement.SpaceAround
|
||||||
) {
|
) {
|
||||||
if (orientationHeight == .42f) {
|
|
||||||
Spacer(modifier = Modifier.fillMaxHeight(.05f))
|
|
||||||
}
|
|
||||||
repeat(boardSize) { row ->
|
repeat(boardSize) { row ->
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(orientationWidth),
|
Modifier
|
||||||
|
.fillMaxWidth(),
|
||||||
horizontalArrangement = Arrangement.SpaceAround
|
horizontalArrangement = Arrangement.SpaceAround
|
||||||
) {
|
) {
|
||||||
repeat(boardSize) { col ->
|
repeat(boardSize) { col ->
|
||||||
Button(
|
Button(
|
||||||
onClick = { onButtonClick(row, col) },
|
onClick = { onButtonClick(grid[row][col]) },
|
||||||
modifier = Modifier.size(75.dp),
|
modifier = Modifier.size(75.dp),
|
||||||
enabled = buttonEnabled,
|
enabled = buttonEnabled,
|
||||||
shape = CircleShape
|
shape = CircleShape
|
||||||
) {
|
) {
|
||||||
playerValue(row, col)?.let { player ->
|
playerValue(grid[row][col])?.let { player ->
|
||||||
if (player) {
|
if (player) {
|
||||||
CircleIcon()
|
CircleIcon()
|
||||||
}else if(!player){
|
} else {
|
||||||
CrossIcon()
|
CrossIcon()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
package es.genol.tictactoe.ui.state
|
||||||
|
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.runtime.toMutableStateList
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import es.genol.tictactoe.GameChecks
|
||||||
|
import es.genol.tictactoe.data.model.Ficha
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
class GameState : ViewModel() {
|
||||||
|
private var _gridState = MutableList(9) { Ficha() }.toMutableStateList()
|
||||||
|
val gridState get() = _gridState.toList()
|
||||||
|
private var currentPlayer = ramdomPlayer()
|
||||||
|
|
||||||
|
var isWinner by mutableStateOf(false)
|
||||||
|
private set
|
||||||
|
|
||||||
|
fun gridMarkPlayer(gridId: Int) {
|
||||||
|
if (_gridState[gridId].player == null) {
|
||||||
|
_gridState[gridId] = _gridState[gridId].copy(player = currentPlayer)
|
||||||
|
isWinner = GameChecks(currentPlayer, gridData = gridState).playerWinnerCheck()
|
||||||
|
currentPlayer = !currentPlayer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun gridClean() {
|
||||||
|
repeat(9) { _gridState[it] = Ficha() }
|
||||||
|
currentPlayer = ramdomPlayer()
|
||||||
|
isWinner = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun ramdomPlayer() = (Random.nextBits(bitCount = 1) > 0)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,77 +0,0 @@
|
||||||
package es.genol.tictactoe.ui.state
|
|
||||||
|
|
||||||
import androidx.compose.material3.SnackbarResult
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateListOf
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.lifecycle.ViewModel
|
|
||||||
import es.genol.tictactoe.data.model.Ficha
|
|
||||||
import kotlin.random.Random
|
|
||||||
|
|
||||||
class TicTacToeViewModel : ViewModel() {
|
|
||||||
private var buttonStateList = mutableStateListOf<Ficha>()
|
|
||||||
private var playerChange = ramdomPlayer()
|
|
||||||
|
|
||||||
var isWinner by mutableStateOf(false)
|
|
||||||
private set
|
|
||||||
|
|
||||||
|
|
||||||
init {
|
|
||||||
fillBoardGame()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun printPosition(row: Int, col: Int) {
|
|
||||||
val index = buttonStateList.indexOf(buttonStateList.find { (it.row == row && it.col == col) })
|
|
||||||
if (buttonStateList[index].player == null) {
|
|
||||||
buttonStateList[index] = buttonStateList[index].copy(player = playerChange)
|
|
||||||
isWinner = horizontalCheck(player = playerChange)
|
|
||||||
playerChange = !playerChange
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getPlayer(row: Int, col: Int): Boolean? {
|
|
||||||
return buttonStateList.find { (it.row == row && it.col == col) }?.player
|
|
||||||
}
|
|
||||||
|
|
||||||
fun boardReboot() {
|
|
||||||
buttonStateList.clear()
|
|
||||||
playerChange = ramdomPlayer()
|
|
||||||
isWinner = false
|
|
||||||
fillBoardGame()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun fillBoardGame() {
|
|
||||||
for (row in 0..2) {
|
|
||||||
for (col in 0..2) {
|
|
||||||
buttonStateList.add(
|
|
||||||
Ficha(row, col, null)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun ramdomPlayer() = (Random.nextBits(bitCount = 1) == 1)
|
|
||||||
|
|
||||||
private fun horizontalCheck(player: Boolean): Boolean {
|
|
||||||
var result: Int
|
|
||||||
for (i in 0..6 step 3) {
|
|
||||||
result = 0
|
|
||||||
for (n in i..i + 2) {
|
|
||||||
if (buttonStateList[n].player == player){
|
|
||||||
result++
|
|
||||||
}
|
|
||||||
if (result == 3) return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
fun resetFromSnackBar(result: SnackbarResult){
|
|
||||||
when (result) {
|
|
||||||
SnackbarResult.Dismissed -> {}
|
|
||||||
SnackbarResult.ActionPerformed -> { boardReboot() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Loading…
Reference in New Issue