Added winner player checks

This commit is contained in:
Salatiel Genol 2023-05-19 23:01:10 +02:00
parent 19df47972a
commit db61c25b03
6 changed files with 139 additions and 144 deletions

View File

@ -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
}
}

View File

@ -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)

View File

@ -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(
TopAppBar( topBar = {
title = { Text(text = "TicTacToe") }, TopAppBar(
actions = { title = { Text(text = "TicTacToe") },
Button(onClick = { viewModel.boardReboot() }) { actions = {
Text(text = "REINICIAR") Button(onClick = { viewModel.gridClean() }) {
} 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)
} }
} }
} }

View File

@ -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()
} }
} }

View File

@ -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)
}

View File

@ -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() }
}
}
}