完成组合掷骰功能和 Android 支持

This commit is contained in:
2026-10-07 17:23:47 +08:00
parent bfdff74c9d
commit 79b4f12a14
11 changed files with 328 additions and 39 deletions
+18 -1
View File
@@ -1,4 +1,7 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
alias(libs.plugins.androidKmpLibrary)
alias(libs.plugins.kotlinMultiplatform)
alias(libs.plugins.composeMultiplatform)
alias(libs.plugins.composeCompiler)
@@ -7,6 +10,20 @@ plugins {
kotlin {
jvm()
android {
namespace = "cn.itouc.git.diceroller.shared"
compileSdk = 37
minSdk = 24
compilerOptions {
jvmTarget.set(JvmTarget.JVM_17)
}
androidResources {
enable = true
}
}
sourceSets {
commonMain.dependencies {
implementation(libs.compose.runtime)
@@ -22,4 +39,4 @@ kotlin {
implementation(libs.kotlin.test)
}
}
}
}
@@ -1,56 +1,229 @@
package cn.itouc.git.diceroller
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.safeContentPadding
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import org.jetbrains.compose.resources.painterResource
import diceroller.shared.generated.resources.Res
import diceroller.shared.generated.resources.compose_multiplatform
import androidx.compose.ui.unit.dp
import kotlin.random.Random
data class DiceGroup(
val count: Int,
val sides: Int
)
data class DiceGroupResult(
val group: DiceGroup,
val points: List<Int>
) {
val total: Long
get() = points.sumOf { it.toLong() }
}
@Composable
@Preview
fun App() {
MaterialTheme {
var showContent by remember { mutableStateOf(false) }
var randomInt by remember { mutableStateOf(Random.nextInt()) }
var sidesInput by remember { mutableStateOf("6") }
var countInput by remember { mutableStateOf("1") }
var groups by remember {
mutableStateOf(emptyList<DiceGroup>())
}
var results by remember {
mutableStateOf(emptyList<DiceGroupResult>())
}
val diceSides = sidesInput.toIntOrNull()
val diceCount = countInput.toIntOrNull()
val validSides = diceSides != null &&
diceSides >= 1 &&
diceSides < Int.MAX_VALUE
val validCount = diceCount != null &&
diceCount in 1..1000
val totalDiceCount = groups.sumOf { it.count }
val withinLimit = diceCount != null &&
diceCount in 1..1000 &&
totalDiceCount + diceCount <= 1000
Column(
modifier = Modifier
.background(MaterialTheme.colorScheme.primaryContainer)
.safeContentPadding()
.fillMaxSize(),
.fillMaxSize()
.verticalScroll(rememberScrollState())
.padding(24.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
Text(
text = "组合掷骰子",
style = MaterialTheme.typography.headlineMedium
)
OutlinedTextField(
value = sidesInput,
onValueChange = { sidesInput = it },
label = { Text("骰子面数") },
singleLine = true,
isError = !validSides
)
if (!validSides) {
Text(
text = "面数请输入 1~2147483646 的整数",
color = MaterialTheme.colorScheme.error
)
}
OutlinedTextField(
value = countInput,
onValueChange = { countInput = it },
label = { Text("骰子数量") },
singleLine = true,
isError = !validCount
)
if (!validCount) {
Text(
text = "数量请输入 1~1000 的整数",
color = MaterialTheme.colorScheme.error
)
} else if (!withinLimit) {
Text(
text = "添加后将超过总数限制,最多允许 1000 个骰子",
color = MaterialTheme.colorScheme.error
)
}
Button(
enabled = validSides && validCount && withinLimit,
onClick = {
showContent = !showContent
if (showContent) randomInt = Random.nextInt()
val sides = diceSides
val count = diceCount
if (
sides != null &&
sides >= 1 &&
sides < Int.MAX_VALUE &&
count != null &&
count in 1..1000 &&
groups.sumOf { it.count } + count <= 1000
) {
groups = groups + DiceGroup(
count = count,
sides = sides
)
results = emptyList()
}
}
) {
Text("Click me!")
Text("添加骰子组")
}
AnimatedVisibility(showContent) {
Column(
modifier = Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Image(painterResource(Res.drawable.compose_multiplatform), null)
Text("a random int: $randomInt")
Text(
text = "当前组合",
style = MaterialTheme.typography.titleLarge
)
if (groups.isEmpty()) {
Text("还没有骰子组,请先添加")
} else {
groups.forEachIndexed { index, group ->
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
Text(
text = "第 ${index + 1} 组:${group.count}D${group.sides}",
modifier = Modifier.weight(1f)
)
TextButton(
onClick = {
groups = groups.filterIndexed { groupIndex, _ ->
groupIndex != index
}
results = emptyList()
}
) {
Text("删除")
}
}
}
}
Text("共 ${groups.size} 组,$totalDiceCount / 1000 个骰子")
Button(
enabled = groups.isNotEmpty(),
onClick = {
results = groups.map { group ->
DiceGroupResult(
group = group,
points = List(group.count) {
Random.nextInt(1, group.sides + 1)
}
)
}
}
) {
Text("全部掷骰")
}
if (results.isNotEmpty()) {
Text(
text = "掷骰结果",
style = MaterialTheme.typography.titleLarge
)
results.forEachIndexed { index, result ->
Column(
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
Text(
text = "第 ${index + 1} 组:" +
"${result.group.count}D${result.group.sides}",
style = MaterialTheme.typography.titleMedium
)
val pointsText = result.points
.take(20)
.joinToString(" + ")
val remainingHint =
if (result.points.size > 20) {
" + …"
} else {
""
}
Text("点数:$pointsText$remainingHint")
Text("小计:${result.total}")
}
}
val grandTotal = results.sumOf { it.total }
Text(
text = "总和:$grandTotal",
style = MaterialTheme.typography.headlineMedium
)
}
}
}
}