Reputation: 11
I am working on a puzzle game project and I already have 1 empty cell which swaps places with all cells next to it but I want there to be 2 emptycells. I have tried everything I could to no avail. Is it possible to achieve this in jetpackcompose?
I tried creating 2 empty cells but only one worked
Upvotes: 1
Views: 80
Reputation: 171
Yes, you can create a puzzle game with multiple empty cells that swap places with neighboring cells. To achieve this, you need to manage the state of your puzzle cells and update them accordingly. You can see the bellow code which might help you understand how to handle two empty cells in a puzzle grid:
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.SurfaceColor
import androidx.compose.material3.icons.Icons
import androidx.compose.material3.icons.filled.Refresh
import androidx.compose.material3.icons.filled.SwapHoriz
import androidx.compose.material3.icons.filled.SwapVert
import androidx.compose.material3.materialIcon
import androidx.compose.material3.rememberAppBarConfiguration
import androidx.compose.material3.rememberBottomSheetState
import androidx.compose.material3.rememberScaffoldState
import androidx.compose.material3.rememberSwipeableState
import androidx.compose.material3.swipeable
import androidx.compose.material3.textfield.TextFieldDefaults
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.example.featherandroidtasks.ui.theme.FeatherAndroidTasksTheme
data class PuzzleCell(
val value: Int,
val isEmpty: Boolean,
)
@Composable
fun PuzzleGame() {
var puzzleCells by remember { mutableStateOf(generateInitialPuzzle()) }
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Grid(cells = puzzleCells) { row, col ->
onCellClick(row, col, puzzleCells)
}
Spacer(modifier = Modifier.height(16.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically
) {
IconButton(
onClick = {
puzzleCells = generateInitialPuzzle()
}
) {
Icon(imageVector = Icons.Default.Refresh, contentDescription = "Restart")
}
}
}
}
@Composable
fun Grid(
cells: List<List<PuzzleCell>>,
onCellClick: (row: Int, col: Int) -> Unit
) {
Column {
for (row in cells.indices) {
Row {
for (col in cells[row].indices) {
PuzzleCell(
cell = cells[row][col],
onCellClick = { onCellClick(row, col) }
)
}
}
}
}
}
@Composable
fun PuzzleCell(
cell: PuzzleCell,
onCellClick: () -> Unit
) {
Box(
modifier = Modifier
.size(50.dp)
.clickable { onCellClick() }
.background(MaterialTheme.colorScheme.primary)
.clip(CircleShape)
) {
if (!cell.isEmpty) {
Text(
text = cell.value.toString(),
color = MaterialTheme.colorScheme.onPrimary,
modifier = Modifier
.align(Alignment.Center)
.padding(8.dp)
)
}
}
}
fun generateInitialPuzzle(): List<List<PuzzleCell>> {
val size = 4
val values = (1..size * size - 1).shuffled() + listOf(0) // 0 represents the empty cell
return values.chunked(size)
.mapIndexed { row, list ->
list.mapIndexed { col, value ->
PuzzleCell(value = value, isEmpty = value == 0)
}
}
}
fun moveEmptyCell(
puzzleCells: List<List<PuzzleCell>>,
emptyCellRow: Int,
emptyCellCol: Int,
newRow: Int,
newCol: Int
): List<List<PuzzleCell>> {
return puzzleCells.mapIndexed { row, list ->
list.mapIndexed { col, cell ->
if ((row == emptyCellRow && col == emptyCellCol) ||
(row == newRow && col == newCol)
) {
PuzzleCell(value = cell.value, isEmpty = !cell.isEmpty)
} else {
cell
}
}
}
}
@Composable
fun onCellClick(row: Int, col: Int, puzzleCells: List<List<PuzzleCell>>) {
val emptyCell = puzzleCells.flatten().first { it.isEmpty }
val emptyCellRow = puzzleCells.indexOfFirst { it.contains(emptyCell) }
val emptyCellCol = puzzleCells[emptyCellRow].indexOf(emptyCell)
// Check if the clicked cell is a neighbor of the empty cell
if ((row == emptyCellRow && (col == emptyCellCol - 1 || col == emptyCellCol + 1)) ||
(col == emptyCellCol && (row == emptyCellRow - 1 || row == emptyCellRow + 1))
) {
puzzleCells = moveEmptyCell(puzzleCells, emptyCellRow, emptyCellCol, row, col)
}
}
@Preview(showBackground = true)
@Composable
fun PuzzleGamePreview() {
PuzzleGame()
}
In above, I've created a PuzzleCell
data class to represent each cell in the puzzle grid. The PuzzleGame
composable function manages the state of the puzzle cells and provides a grid with clickable cells. The onCellClick
function handles the logic for swapping the empty cell with its neighbors.
Moreover, this is a simple example, and you may need to adapt it to your specific puzzle game requirements. Additionally, you might want to consider using more advanced state management solutions, depending on the complexity of your game.
Please, let me know whether it works, Hope it helps!
Upvotes: 0