fix : 修复积分榜问题

This commit is contained in:
flykhan 2026-04-26 11:01:16 +08:00
parent 1a215a4b8f
commit d201ae48eb
4 changed files with 175 additions and 42 deletions

View File

@ -21,8 +21,15 @@ import com.unogame.ui.theme.*
fun GameOverScreen( fun GameOverScreen(
winnerName: String, winnerName: String,
isYouWinner: Boolean, isYouWinner: Boolean,
onBackToMenu: () -> Unit onBackToMenu: () -> Unit,
points: Int = 0,
difficulty: String = "",
duration: Int = 0,
turnNumber: Int = 0,
playerCount: Int = 0
) { ) {
val durationText = if (duration >= 60) "${duration / 60}${duration % 60}" else "${duration}"
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@ -61,6 +68,38 @@ fun GameOverScreen(
textAlign = TextAlign.Center textAlign = TextAlign.Center
) )
if (isYouWinner && points > 0) {
Spacer(modifier = Modifier.height(24.dp))
Text(
text = "+$points",
fontSize = 32.sp,
fontWeight = FontWeight.Black,
color = GoldAccent
)
Spacer(modifier = Modifier.height(12.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text("难度", color = Color.White.copy(alpha = 0.5f), fontSize = 12.sp)
Text(difficulty, color = Color.White, fontSize = 14.sp, fontWeight = FontWeight.Bold)
}
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text("时长", color = Color.White.copy(alpha = 0.5f), fontSize = 12.sp)
Text(durationText, color = Color.White, fontSize = 14.sp, fontWeight = FontWeight.Bold)
}
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text("人数", color = Color.White.copy(alpha = 0.5f), fontSize = 12.sp)
Text("${playerCount}", color = Color.White, fontSize = 14.sp, fontWeight = FontWeight.Bold)
}
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text("轮次", color = Color.White.copy(alpha = 0.5f), fontSize = 12.sp)
Text("$turnNumber", color = Color.White, fontSize = 14.sp, fontWeight = FontWeight.Bold)
}
}
}
Spacer(modifier = Modifier.height(48.dp)) Spacer(modifier = Modifier.height(48.dp))
Button( Button(

View File

@ -14,10 +14,19 @@ import com.unogame.model.*
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
data class ScoreEntry(val name: String, val wins: Int, val mode: String) data class ScoreEntry(
val name: String,
val mode: String,
val points: Int,
val difficulty: String,
val duration: Int,
val playerCount: Int,
val turnNumber: Int,
val date: Long
)
object Scoreboard { object Scoreboard {
private const val PREFS_KEY = "uno_scores" private const val PREFS_KEY = "uno_scoreboard"
private val gson = Gson() private val gson = Gson()
fun loadScores(context: Context): List<ScoreEntry> { fun loadScores(context: Context): List<ScoreEntry> {
@ -33,15 +42,30 @@ object Scoreboard {
.edit().putString(PREFS_KEY, gson.toJson(scores)).apply() .edit().putString(PREFS_KEY, gson.toJson(scores)).apply()
} }
fun addWin(context: Context, name: String, mode: GameMode) { fun addEntry(
context: Context,
name: String,
mode: String,
points: Int,
difficulty: String,
duration: Int,
playerCount: Int,
turnNumber: Int
) {
val scores = loadScores(context).toMutableList() val scores = loadScores(context).toMutableList()
val existing = scores.indexOfFirst { it.name == name && it.mode == mode.displayName } scores.add(
if (existing >= 0) { ScoreEntry(
scores[existing] = scores[existing].copy(wins = scores[existing].wins + 1) name = name,
} else { mode = mode,
scores.add(ScoreEntry(name, 1, mode.displayName)) points = points,
} difficulty = difficulty,
saveScores(context, scores.sortedByDescending { it.wins }.take(20)) duration = duration,
playerCount = playerCount,
turnNumber = turnNumber,
date = System.currentTimeMillis()
)
)
saveScores(context, scores.sortedByDescending { it.points }.take(50))
} }
} }
@ -57,6 +81,7 @@ fun LocalGameScreen(
val rules = remember { GameRules.forMode(mode) } val rules = remember { GameRules.forMode(mode) }
val engine = remember { GameEngine(rules) } val engine = remember { GameEngine(rules) }
val aiDiff = remember { AIDifficulty.load(context) } val aiDiff = remember { AIDifficulty.load(context) }
val gameStartTime = remember { System.currentTimeMillis() }
val players = remember { val players = remember {
val list = mutableListOf<Player>() val list = mutableListOf<Player>()
@ -75,6 +100,10 @@ fun LocalGameScreen(
var winnerName by remember { mutableStateOf("") } var winnerName by remember { mutableStateOf("") }
var isYouWinner by remember { mutableStateOf(false) } var isYouWinner by remember { mutableStateOf(false) }
var scoreSaved by remember { mutableStateOf(false) } var scoreSaved by remember { mutableStateOf(false) }
var gamePoints by remember { mutableIntStateOf(0) }
var gameDuration by remember { mutableIntStateOf(0) }
var gameDifficulty by remember { mutableStateOf("") }
var gameTurnNumber by remember { mutableIntStateOf(0) }
val myPlayerId = "human" val myPlayerId = "human"
val currentPlayer = gameState.currentPlayer val currentPlayer = gameState.currentPlayer
@ -90,7 +119,25 @@ fun LocalGameScreen(
isYouWinner = state.winner?.id == myPlayerId isYouWinner = state.winner?.id == myPlayerId
if (isYouWinner && !scoreSaved) { if (isYouWinner && !scoreSaved) {
scoreSaved = true scoreSaved = true
Scoreboard.addWin(context, humanPlayerName, mode) gameTurnNumber = state.turnNumber
gameDuration = ((System.currentTimeMillis() - gameStartTime) / 1000).toInt()
gameDifficulty = aiDiff.displayName
gamePoints = state.players.filter { it.id != myPlayerId }.sumOf { player ->
player.cards.sumOf { card ->
val active = if (state.flipped && card.flipSide != null) card.flipSide else card
active.score
}
}
Scoreboard.addEntry(
context = context,
name = humanPlayerName,
mode = mode.displayName,
points = gamePoints,
difficulty = gameDifficulty,
duration = gameDuration,
playerCount = totalPlayers,
turnNumber = gameTurnNumber
)
} }
} }
} }
@ -145,7 +192,12 @@ fun LocalGameScreen(
GameOverScreen( GameOverScreen(
winnerName = winnerName, winnerName = winnerName,
isYouWinner = isYouWinner, isYouWinner = isYouWinner,
onBackToMenu = onBackToMenu onBackToMenu = onBackToMenu,
points = gamePoints,
difficulty = gameDifficulty,
duration = gameDuration,
turnNumber = gameTurnNumber,
playerCount = totalPlayers
) )
} else { } else {
GameScreen( GameScreen(

View File

@ -1,7 +1,9 @@
package com.unogame.ui.screens package com.unogame.ui.screens
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
@ -19,6 +21,8 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.unogame.game.GameMode import com.unogame.game.GameMode
import com.unogame.ui.theme.* import com.unogame.ui.theme.*
import java.text.SimpleDateFormat
import java.util.*
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
@ -48,7 +52,9 @@ fun ScoreboardScreen(onBack: () -> Unit) {
// Mode filter chips // Mode filter chips
if (scores.isNotEmpty()) { if (scores.isNotEmpty()) {
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier
.fillMaxWidth()
.horizontalScroll(rememberScrollState()),
horizontalArrangement = Arrangement.spacedBy(8.dp) horizontalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
modes.forEach { mode -> modes.forEach { mode ->
@ -95,41 +101,75 @@ fun ScoreboardScreen(onBack: () -> Unit) {
2 -> Color(0xFFCD7F32) 2 -> Color(0xFFCD7F32)
else -> Color.White.copy(alpha = 0.5f) else -> Color.White.copy(alpha = 0.5f)
} }
val dateFormat = remember { SimpleDateFormat("MM-dd HH:mm", Locale.getDefault()) }
val durationText = if (entry.duration >= 60) "${entry.duration / 60}${entry.duration % 60}"
else "${entry.duration}"
Card( Card(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(12.dp), shape = RoundedCornerShape(12.dp),
colors = CardDefaults.cardColors(containerColor = if (index < 3) DarkSurface else DarkCard) colors = CardDefaults.cardColors(containerColor = if (index < 3) DarkSurface else DarkCard)
) { ) {
Row( Column(modifier = Modifier.padding(12.dp)) {
modifier = Modifier.padding(12.dp),
verticalAlignment = Alignment.CenterVertically
) {
// Rank
Text(
text = "#${index + 1}",
color = rankColor,
fontSize = 18.sp,
fontWeight = FontWeight.Black,
modifier = Modifier.width(44.dp)
)
// Name
Column(modifier = Modifier.weight(1f)) {
Text(entry.name, color = Color.White, fontWeight = FontWeight.Bold, fontSize = 15.sp)
Text(
entry.mode,
color = Color.White.copy(alpha = 0.5f),
fontSize = 12.sp
)
}
// Wins
Row(verticalAlignment = Alignment.CenterVertically) { Row(verticalAlignment = Alignment.CenterVertically) {
Icon(Icons.Default.Star, null, tint = GoldAccent, modifier = Modifier.size(18.dp))
Spacer(modifier = Modifier.width(4.dp))
Text( Text(
"${entry.wins}", text = "#${index + 1}",
color = GoldAccent, color = rankColor,
fontWeight = FontWeight.Bold, fontSize = 18.sp,
fontSize = 16.sp fontWeight = FontWeight.Black,
modifier = Modifier.width(44.dp)
)
Column(modifier = Modifier.weight(1f)) {
Text(entry.name, color = Color.White, fontWeight = FontWeight.Bold, fontSize = 15.sp)
Text(
entry.mode,
color = GoldAccent,
fontSize = 12.sp
)
}
Column(horizontalAlignment = Alignment.End) {
Text(
"${entry.points}",
color = GoldAccent,
fontWeight = FontWeight.Black,
fontSize = 20.sp
)
}
}
Spacer(modifier = Modifier.height(8.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(Icons.Default.SmartToy, null, tint = Color.White.copy(alpha = 0.5f), modifier = Modifier.size(14.dp))
Spacer(modifier = Modifier.width(2.dp))
Text(entry.difficulty, color = Color.White.copy(alpha = 0.5f), fontSize = 11.sp)
}
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(Icons.Default.Timer, null, tint = Color.White.copy(alpha = 0.5f), modifier = Modifier.size(14.dp))
Spacer(modifier = Modifier.width(2.dp))
Text(durationText, color = Color.White.copy(alpha = 0.5f), fontSize = 11.sp)
}
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(Icons.Default.Group, null, tint = Color.White.copy(alpha = 0.5f), modifier = Modifier.size(14.dp))
Spacer(modifier = Modifier.width(2.dp))
Text("${entry.playerCount}", color = Color.White.copy(alpha = 0.5f), fontSize = 11.sp)
}
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(Icons.Default.Loop, null, tint = Color.White.copy(alpha = 0.5f), modifier = Modifier.size(14.dp))
Spacer(modifier = Modifier.width(2.dp))
Text("${entry.turnNumber}", color = Color.White.copy(alpha = 0.5f), fontSize = 11.sp)
}
}
Spacer(modifier = Modifier.height(4.dp))
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(Icons.Default.Schedule, null, tint = Color.White.copy(alpha = 0.3f), modifier = Modifier.size(12.dp))
Spacer(modifier = Modifier.width(2.dp))
Text(
dateFormat.format(Date(entry.date)),
color = Color.White.copy(alpha = 0.3f),
fontSize = 10.sp
) )
} }
} }

2
gradlew vendored Executable file
View File

@ -0,0 +1,2 @@
#!/bin/sh
exec java -jar "$(dirname "$0")/gradle/wrapper/gradle-wrapper.jar" "$@"