Created
July 7, 2025 16:31
-
-
Save ardakazanci/425a364f26a72b4e087e1336bd537f11 to your computer and use it in GitHub Desktop.
RockPaperScissors Jetpack Compose
This file contains hidden or 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
@Stable | |
data class Particle( | |
val id: Int, // stable key | |
val type: MutableState<RPS>, | |
val x: MutableState<Float>, | |
val y: MutableState<Float>, | |
val dx: MutableState<Float>, | |
val dy: MutableState<Float> | |
) | |
@Composable | |
fun RockPaperScissorsGame(modifier: Modifier) { | |
val particleSizeDp = 48.dp | |
val particleSizePx = with(LocalDensity.current) { particleSizeDp.toPx() } | |
val rockPainter = painterResource(id = R.drawable.stone) | |
val paperPainter = painterResource(id = R.drawable.paper) | |
val scissorsPainter = painterResource(id = R.drawable.scissors) | |
val particles = remember { | |
mutableStateListOf<Particle>().apply { | |
repeat(15) { | |
add(randomParticle(RPS.ROCK, it)) | |
} | |
repeat(15) { | |
add(randomParticle(RPS.PAPER, 100 + it)) | |
} | |
repeat(15) { | |
add(randomParticle(RPS.SCISSORS, 200 + it)) | |
} | |
} | |
} | |
var gameWidth by remember { mutableStateOf(0f) } | |
var gameHeight by remember { mutableStateOf(0f) } | |
LaunchedEffect(Unit) { | |
while (true) { | |
particles.forEach { p -> | |
p.x.value += p.dx.value | |
p.y.value += p.dy.value | |
// impact effect | |
if (p.x.value < 0) { | |
p.x.value = 0f | |
p.dx.value *= -1 | |
} | |
if (p.x.value > gameWidth - particleSizePx) { | |
p.x.value = gameWidth - particleSizePx | |
p.dx.value *= -1 | |
} | |
if (p.y.value < 0) { | |
p.y.value = 0f | |
p.dy.value *= -1 | |
} | |
if (p.y.value > gameHeight - particleSizePx) { | |
p.y.value = gameHeight - particleSizePx | |
p.dy.value *= -1 | |
} | |
} | |
// Collision | |
particles.forEach { p1 -> | |
particles.forEach { p2 -> | |
if (p1 != p2 && | |
distance(p1.x.value, p1.y.value, p2.x.value, p2.y.value) < particleSizePx | |
) { | |
val winner = winner(p1.type.value, p2.type.value) | |
if (winner != null) { | |
if (winner == p1.type.value) p2.type.value = winner | |
else p1.type.value = winner | |
} | |
} | |
} | |
} | |
delay(16) | |
} | |
} | |
Column { | |
Row( | |
modifier | |
.fillMaxWidth() | |
.height(56.dp), | |
horizontalArrangement = Arrangement.SpaceEvenly | |
) { | |
Text( | |
"Rock: ${particles.count { it.type.value == RPS.ROCK }}", | |
fontSize = 24.sp | |
) | |
Text( | |
"Paper: ${particles.count { it.type.value == RPS.PAPER }}", | |
fontSize = 24.sp | |
) | |
Text( | |
"Scissors: ${particles.count { it.type.value == RPS.SCISSORS }}", | |
fontSize = 24.sp | |
) | |
} | |
Box( | |
Modifier | |
.fillMaxSize() | |
.onGloballyPositioned { | |
gameWidth = it.size.width.toFloat() | |
gameHeight = it.size.height.toFloat() | |
} | |
) { | |
particles.forEach { p -> | |
val painter = when (p.type.value) { | |
RPS.ROCK -> rockPainter | |
RPS.PAPER -> paperPainter | |
RPS.SCISSORS -> scissorsPainter | |
} | |
Image( | |
painter = painter, | |
contentDescription = null, | |
modifier = Modifier | |
.size(particleSizeDp) | |
.offset { | |
IntOffset( | |
p.x.value.toInt(), | |
p.y.value.toInt() | |
) | |
} | |
) | |
} | |
} | |
} | |
} | |
// The winning rate of scissors will be very high :) | |
fun randomParticle(type: RPS, id: Int): Particle { | |
val (xRange, yRange) = when (type) { | |
RPS.ROCK -> { | |
// Bottom Left random position | |
(50..300) to (800..1100) | |
} | |
RPS.PAPER -> { | |
// Top Right r-p | |
(800..1100) to (50..300) | |
} | |
RPS.SCISSORS -> { | |
// Bottom Right r-p | |
(800..1100) to (800..1100) | |
} | |
} | |
return Particle( | |
id = id, | |
type = mutableStateOf(type), | |
x = mutableFloatStateOf(xRange.random().toFloat()), | |
y = mutableFloatStateOf(yRange.random().toFloat()), | |
dx = mutableFloatStateOf(listOf(-4f, -3f, 3f, 4f).random()), | |
dy = mutableFloatStateOf(listOf(-4f, -3f, 3f, 4f).random()) | |
) | |
} | |
fun winner(a: RPS, b: RPS): RPS? { | |
return when { | |
a == b -> null | |
a == RPS.ROCK && b == RPS.SCISSORS -> a | |
a == RPS.SCISSORS && b == RPS.PAPER -> a | |
a == RPS.PAPER && b == RPS.ROCK -> a | |
b == RPS.ROCK && a == RPS.SCISSORS -> b | |
b == RPS.SCISSORS && a == RPS.PAPER -> b | |
b == RPS.PAPER && a == RPS.ROCK -> b | |
else -> null | |
} | |
} | |
fun distance(x1: Float, y1: Float, x2: Float, y2: Float): Float { | |
return sqrt((x1 - x2).pow(2) + (y1 - y2).pow(2)) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment