Created
January 8, 2024 16:49
-
-
Save ardakazanci/ed9cbca5da1df610c1430e7648b8f46f to your computer and use it in GitHub Desktop.
Tic Tac Toe - Jetpack Compose
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class MainActivity : ComponentActivity() { | |
override fun onCreate(savedInstanceState: Bundle?) { | |
super.onCreate(savedInstanceState) | |
enableEdgeToEdge() | |
setContent { | |
TicTacToeGameTheme { | |
TicTacToeGame() | |
} | |
} | |
} | |
} | |
@Composable | |
fun TicTacToeGame() { | |
val gridSize = 3 | |
val cellSizeDp = 200.dp / gridSize | |
val cellSizePx = with(LocalDensity.current) { cellSizeDp.toPx() } | |
var grid by remember { mutableStateOf(Array(gridSize) { CharArray(gridSize) { ' ' } }) } | |
var currentPlayer by remember { mutableStateOf('X') } | |
var winner by remember { mutableStateOf<Char?>(null) } | |
var animationOffset by remember { mutableStateOf(0.dp) } | |
LaunchedEffect(winner) { | |
if (winner != null) { | |
animationOffset = -300.dp | |
} | |
} | |
Column( | |
modifier = Modifier.fillMaxSize(), | |
verticalArrangement = Arrangement.Center, | |
horizontalAlignment = Alignment.CenterHorizontally | |
) { | |
Canvas(modifier = Modifier | |
.size(200.dp) | |
.pointerInput(Unit) { | |
detectTapGestures { offset -> | |
val x = (offset.x / cellSizePx).toInt() | |
val y = (offset.y / cellSizePx).toInt() | |
if (grid[y][x] == ' ' && winner == null) { | |
grid = grid.copyOf().also { it[y][x] = currentPlayer } | |
currentPlayer = if (currentPlayer == 'X') 'O' else 'X' | |
winner = checkWinner(grid) | |
} | |
} | |
}) { | |
val gradientBrush = Brush.linearGradient( | |
colors = listOf(Color.Yellow, Color.Magenta) | |
) | |
for (i in 1 until gridSize) { | |
drawLine( | |
brush = gradientBrush, | |
start = Offset(x = cellSizePx * i, y = 0f), | |
end = Offset(x = cellSizePx * i, y = 200.dp.toPx()), | |
strokeWidth = 4.dp.toPx() | |
) | |
drawLine( | |
brush = gradientBrush, | |
start = Offset(x = 0f, y = cellSizePx * i), | |
end = Offset(x = 200.dp.toPx(), y = cellSizePx * i), | |
strokeWidth = 4.dp.toPx() | |
) | |
} | |
for (i in 0 until gridSize) { | |
for (j in 0 until gridSize) { | |
val offsetX = cellSizePx * j | |
val offsetY = cellSizePx * i | |
when (grid[i][j]) { | |
'X' -> drawX(offsetX, offsetY, cellSizePx) | |
'O' -> drawO(offsetX, offsetY, cellSizePx) | |
} | |
} | |
} | |
} | |
AnimatedVisibility(visible = winner != null) { | |
Row( | |
verticalAlignment = Alignment.Top, | |
horizontalArrangement = Arrangement.Center, | |
modifier = Modifier.fillMaxWidth() | |
) { | |
Text( | |
text = "Winner: ${winner ?: ""}", | |
fontSize = 24.sp, | |
fontWeight = FontWeight.Bold, | |
color = Color.Red, | |
modifier = Modifier.padding(16.dp) | |
) | |
} | |
} | |
} | |
} | |
private fun checkWinner(grid: Array<CharArray>): Char? { | |
for (row in grid) { | |
if (row[0] != ' ' && row[0] == row[1] && row[0] == row[2]) { | |
return row[0] | |
} | |
} | |
for (col in 0..2) { | |
if (grid[0][col] != ' ' && grid[0][col] == grid[1][col] && grid[0][col] == grid[2][col]) { | |
return grid[0][col] | |
} | |
} | |
if (grid[0][0] != ' ' && grid[0][0] == grid[1][1] && grid[0][0] == grid[2][2]) { | |
return grid[0][0] | |
} | |
if (grid[0][2] != ' ' && grid[0][2] == grid[1][1] && grid[0][2] == grid[2][0]) { | |
return grid[0][2] | |
} | |
if (grid.all { row -> row.all { cell -> cell != ' ' } }) { | |
return 'D' | |
} | |
return null | |
} | |
private fun DrawScope.drawX(offsetX: Float, offsetY: Float, size: Float) { | |
val gradientBrush = Brush.linearGradient( | |
colors = listOf(Color.Black, Color.Cyan) | |
) | |
drawLine( | |
brush = gradientBrush, | |
start = Offset(offsetX, offsetY), | |
end = Offset(offsetX + size, offsetY + size), | |
strokeWidth = 14F | |
) | |
drawLine( | |
brush = gradientBrush, | |
start = Offset(offsetX + size, offsetY), | |
end = Offset(offsetX, offsetY + size), | |
strokeWidth = 14F | |
) | |
} | |
private fun DrawScope.drawO(offsetX: Float, offsetY: Float, size: Float) { | |
val gradientBrush = Brush.linearGradient( | |
colors = listOf(Color.Blue, Color.Red) | |
) | |
drawCircle( | |
brush = gradientBrush, | |
radius = size / 2, | |
center = Offset(offsetX + size / 2, offsetY + size / 2), | |
style = Stroke(width = 6.dp.toPx()) | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment