Commit b6616e1f authored by dsq's avatar dsq

300

parent 8e022c00
......@@ -122,6 +122,8 @@ kotlin {
dependencies {
api(libs.compose.multiplatform.export) // 导出compose多平台接口给依赖消费者
}
kotlin.srcDir("src/commonMain/kotlin/testHap")
}
}
}
......@@ -241,3 +243,35 @@ arrayOf("debug", "release").forEach { type ->
}
}
}
// 在现有 100MB+ 项目上继续加码
tasks.register("makeIt300MB") {
doLast {
// 生成 10 万行 @Composable
repeat(1000) { fileIndex ->
File("src/commonMain/kotlin/com/dong/maxhap/demos/hap/Huge$fileIndex.kt").writeText("""
@Composable
fun HugeFunction$fileIndex() {
${(1..100).joinToString("\n") {
"Text(\"$it\", Modifier.padding($it.dp).background(Color($it)))"
}}
}
""".trimIndent())
}
// 生成 5000 个 @Stable 数据类
repeat(5000) { classIndex ->
File("src/commonMain/kotlin/com/dong/maxhap/demos/hap/Stable$classIndex.kt").writeText("""
@Stable
data class StableData$classIndex(
val field1: String,
val field2: Int,
val field3: Boolean,
val field4: List<String>,
val field5: Map<Int, String>
)
""".trimIndent())
}
}
}
\ No newline at end of file
This diff is collapsed.
package com.dong.maxhap.demos.Hap
class hap {
}
\ No newline at end of file
package com.dong.maxhap.demos
// 预生成的大型数据结构1
private val PREGENERATED_LARGE_DATA_1 = generateMassiveData1()
// 大型数据类1 - 用于增加应用体积
data class LargeDataClass1(
val id: Long,
val name: String,
val description: String,
val dataArray: List<String>,
val nestedData: NestedData1
)
data class NestedData1(
val value1: String,
val value2: String,
val value3: String,
val value4: String,
val value5: String
)
// 生成适量数据实例(减少约20%)
fun generateMassiveData1(): List<LargeDataClass1> {
val result = mutableListOf<LargeDataClass1>()
for (i in 0 until 20000) { // 从25000减少到20000,减少5000个对象
result.add(
LargeDataClass1(
id = i.toLong(),
name = "数据对象_" + i,
description = "这是第 " + i + " 个大型数据对象,包含大量文本内容用于增加应用体积。这个描述字段特意设计得很长,以确保占用更多空间。还包括各种特殊字符和数字组合。",
dataArray = listOf(
"数组元素1_数据" + i,
"数组元素2_数据" + i,
"数组元素3_数据" + i,
"数组元素4_数据" + i,
"数组元素5_数据" + i
),
nestedData = NestedData1(
value1 = "嵌套值1_" + i,
value2 = "嵌套值2_" + i,
value3 = "嵌套值3_" + i,
value4 = "嵌套值4_" + i,
value5 = "嵌套值5_" + i
)
)
)
}
return result
}
// 提供访问预生成数据的函数
fun getPreGeneratedData1(): List<LargeDataClass1> = PREGENERATED_LARGE_DATA_1
\ No newline at end of file
package com.dong.maxhap.demos
// 预生成的大型数据结构2
private val PREGENERATED_LARGE_DATA_2 = generateMassiveData2()
// 大型数据类2 - 用于增加应用体积
data class LargeDataClass2(
val identifier: String,
val title: String,
val content: String,
val metadata: Map<String, String>,
val children: List<ChildData2>
)
data class ChildData2(
val childId: Int,
val childName: String,
val childDescription: String
)
// 生成适量数据实例(减少约20%)
fun generateMassiveData2(): List<LargeDataClass2> {
val result = mutableListOf<LargeDataClass2>()
for (i in 0 until 32000) { // 从40000减少到32000,减少8000个对象
val metadata = mutableMapOf<String, String>()
for (j in 0 until 15) {
metadata["meta_" + j] = "元数据_" + i + "_" + j
}
val children = mutableListOf<ChildData2>()
for (k in 0 until 3) {
children.add(
ChildData2(
childId = k,
childName = "子对象_" + i + "_" + k,
childDescription = "描述信息_" + i + "_" + k
)
)
}
result.add(
LargeDataClass2(
identifier = "OBJ_" + i,
title = "对象标题_" + i,
content = "内容数据_" + i + "_大量文本内容用于增加体积",
metadata = metadata,
children = children
)
)
}
return result
}
// 提供访问预生成数据的函数
fun getPreGeneratedData2(): List<LargeDataClass2> = PREGENERATED_LARGE_DATA_2
\ No newline at end of file
package com.dong.maxhap.demos
// 预生成的大型数据结构3
private val PREGENERATED_LARGE_DATA_3 = generateMassiveData3()
// 大型数据类3
data class LargeDataClass3(
val uuid: String,
val label: String,
val details: String,
val attributes: List<String>,
val subItems: List<SubItem3>
)
data class SubItem3(
val subId: Long,
val subLabel: String,
val subDetails: String
)
// 生成适量数据实例(减少约20%)
fun generateMassiveData3(): List<LargeDataClass3> {
val result = mutableListOf<LargeDataClass3>()
for (i in 0 until 28000) { // 从35000减少到28000,减少7000个对象
val attributes = listOf("属性1_" + i, "属性2_" + i, "属性3_" + i, "属性4_" + i)
val subItems = mutableListOf<SubItem3>()
for (j in 0 until 4) {
subItems.add(
SubItem3(
subId = j.toLong(),
subLabel = "子项标签_" + i + "_" + j,
subDetails = "子项详情_" + i + "_" + j
)
)
}
result.add(
LargeDataClass3(
uuid = "UUID_" + i,
label = "标签_" + i,
details = "详细信息_" + i + "_用于增加应用体积的大量数据",
attributes = attributes,
subItems = subItems
)
)
}
return result
}
// 提供访问预生成数据的函数
fun getPreGeneratedData3(): List<LargeDataClass3> = PREGENERATED_LARGE_DATA_3
\ No newline at end of file
package com.dong.maxhap.demos
// 大型数据类4
data class LargeDataClass4(
val recordId: String,
val recordName: String,
val recordDescription: String,
val recordMetadata: Map<String, String>,
val recordItems: List<RecordItem4>
)
data class RecordItem4(
val itemId: Long,
val itemName: String,
val itemDescription: String
)
fun generateMassiveData4(): List<LargeDataClass4> {
val result = mutableListOf<LargeDataClass4>()
for (i in 0 until 25000) {
val metadata = mutableMapOf<String, String>()
for (j in 0 until 20) {
metadata["meta_" + j] = "元数据_" + i + "_" + j
}
val items = mutableListOf<RecordItem4>()
for (k in 0 until 6) {
items.add(
RecordItem4(
itemId = (i * 100 + k).toLong(),
itemName = "项目_" + i + "_" + k,
itemDescription = "项目描述_" + i + "_" + k
)
)
}
result.add(
LargeDataClass4(
recordId = "REC_" + i,
recordName = "记录_" + i,
recordDescription = "记录描述_" + i + "_大量文本内容",
recordMetadata = metadata,
recordItems = items
)
)
}
return result
}
\ No newline at end of file
package com.dong.maxhap.demos
// 大型数据类5
data class LargeDataClass5(
val entityId: String,
val entityName: String,
val entityDetails: String,
val entityAttributes: List<String>,
val entityComponents: List<EntityComponent5>
)
data class EntityComponent5(
val componentId: Int,
val componentName: String,
val componentData: String
)
fun generateMassiveData5(): List<LargeDataClass5> {
val result = mutableListOf<LargeDataClass5>()
for (i in 0 until 20000) {
val attributes = listOf("属性A_" + i, "属性B_" + i, "属性C_" + i, "属性D_" + i, "属性E_" + i)
val components = mutableListOf<EntityComponent5>()
for (j in 0 until 7) {
components.add(
EntityComponent5(
componentId = j,
componentName = "组件_" + i + "_" + j,
componentData = "组件数据_" + i + "_" + j
)
)
}
result.add(
LargeDataClass5(
entityId = "ENTITY_" + i,
entityName = "实体_" + i,
entityDetails = "实体详情_" + i + "_用于增加体积的文本内容",
entityAttributes = attributes,
entityComponents = components
)
)
}
return result
}
\ No newline at end of file
package com.dong.maxhap.demos
// 大型数据类6
data class LargeDataClass6(
val objectId: String,
val objectTitle: String,
val objectContent: String,
val objectTags: List<String>,
val objectSections: List<ObjectSection6>
)
data class ObjectSection6(
val sectionId: Long,
val sectionTitle: String,
val sectionText: String
)
fun generateMassiveData6(): List<LargeDataClass6> {
val result = mutableListOf<LargeDataClass6>()
for (i in 0 until 18000) {
val tags = listOf("标签1_" + i, "标签2_" + i, "标签3_" + i)
val sections = mutableListOf<ObjectSection6>()
for (j in 0 until 5) {
sections.add(
ObjectSection6(
sectionId = j.toLong(),
sectionTitle = "章节_" + i + "_" + j,
sectionText = "章节内容_" + i + "_" + j
)
)
}
result.add(
LargeDataClass6(
objectId = "OBJ_" + i,
objectTitle = "对象_" + i,
objectContent = "对象内容_" + i + "_大量文本数据",
objectTags = tags,
objectSections = sections
)
)
}
return result
}
\ No newline at end of file
package com.dong.maxhap.demos
// 大型数据类7
data class LargeDataClass7(
val itemId: String,
val itemLabel: String,
val itemInfo: String,
val itemProperties: Map<String, String>,
val itemChildren: List<ItemChild7>
)
data class ItemChild7(
val childIndex: Int,
val childLabel: String,
val childInfo: String
)
fun generateMassiveData7(): List<LargeDataClass7> {
val result = mutableListOf<LargeDataClass7>()
for (i in 0 until 15000) {
val properties = mutableMapOf<String, String>()
for (j in 0 until 15) {
properties["prop_" + j] = "属性值_" + i + "_" + j
}
val children = mutableListOf<ItemChild7>()
for (k in 0 until 4) {
children.add(
ItemChild7(
childIndex = k,
childLabel = "子项_" + i + "_" + k,
childInfo = "子项信息_" + i + "_" + k
)
)
}
result.add(
LargeDataClass7(
itemId = "ITEM_" + i,
itemLabel = "项目_" + i,
itemInfo = "项目信息_" + i + "_体积增加数据",
itemProperties = properties,
itemChildren = children
)
)
}
return result
}
\ No newline at end of file
package com.dong.maxhap.demos
// 大型数据类8
data class LargeDataClass8(
val nodeId: String,
val nodeTitle: String,
val nodeDescription: String,
val nodeMetadata: List<String>,
val nodeConnections: List<NodeConnection8>
)
data class NodeConnection8(
val connectionId: Long,
val connectionType: String,
val connectionData: String
)
fun generateMassiveData8(): List<LargeDataClass8> {
val result = mutableListOf<LargeDataClass8>()
for (i in 0 until 12000) {
val metadata = listOf("元数据A_" + i, "元数据B_" + i, "元数据C_" + i)
val connections = mutableListOf<NodeConnection8>()
for (j in 0 until 3) {
connections.add(
NodeConnection8(
connectionId = j.toLong(),
connectionType = "连接类型_" + i + "_" + j,
connectionData = "连接数据_" + i + "_" + j
)
)
}
result.add(
LargeDataClass8(
nodeId = "NODE_" + i,
nodeTitle = "节点_" + i,
nodeDescription = "节点描述_" + i + "_用于增加应用体积",
nodeMetadata = metadata,
nodeConnections = connections
)
)
}
return result
}
\ No newline at end of file
package com.dong.maxhap.demos.game
import kotlin.random.Random
data class Game2048State(
val grid: List<List<Int>>,
val score: Int,
val gameOver: Boolean,
val won: Boolean
)
class Game2048Logic {
companion object {
const val GRID_SIZE = 4
const val WIN_VALUE = 2048
}
fun doInitGrid(): List<List<Int>> {
val grid = List(GRID_SIZE) { List(GRID_SIZE) { 0 } }
return addRandomTile(addRandomTile(grid))
}
fun moveLeft(grid: List<List<Int>>): Pair<List<List<Int>>, Int> {
var score = 0
val newGrid = grid.map { row ->
val filteredRow = row.filter { it != 0 }
val mergedRow = mutableListOf<Int>()
var i = 0
while (i < filteredRow.size) {
if (i < filteredRow.size - 1 && filteredRow[i] == filteredRow[i + 1]) {
val mergedValue = filteredRow[i] * 2
mergedRow.add(mergedValue)
score += mergedValue
i += 2
} else {
mergedRow.add(filteredRow[i])
i++
}
}
// 填充到GRID_SIZE长度
while (mergedRow.size < GRID_SIZE) {
mergedRow.add(0)
}
mergedRow
}
return Pair(newGrid, score)
}
fun moveRight(grid: List<List<Int>>): Pair<List<List<Int>>, Int> {
val reversedGrid = grid.map { it.reversed() }
val (movedGrid, score) = moveLeft(reversedGrid)
val newGrid = movedGrid.map { it.reversed() }
return Pair(newGrid, score)
}
fun moveUp(grid: List<List<Int>>): Pair<List<List<Int>>, Int> {
val transposedGrid = transpose(grid)
val (movedGrid, score) = moveLeft(transposedGrid)
val newGrid = transpose(movedGrid)
return Pair(newGrid, score)
}
fun moveDown(grid: List<List<Int>>): Pair<List<List<Int>>, Int> {
val transposedGrid = transpose(grid)
val (movedGrid, score) = moveRight(transposedGrid)
val newGrid = transpose(movedGrid)
return Pair(newGrid, score)
}
private fun transpose(grid: List<List<Int>>): List<List<Int>> {
return List(GRID_SIZE) { col ->
List(GRID_SIZE) { row ->
grid[row][col]
}
}
}
fun addRandomTile(grid: List<List<Int>>): List<List<Int>> {
val emptyCells = mutableListOf<Pair<Int, Int>>()
for (i in grid.indices) {
for (j in grid[i].indices) {
if (grid[i][j] == 0) {
emptyCells.add(Pair(i, j))
}
}
}
if (emptyCells.isEmpty()) return grid
val (row, col) = emptyCells.random()
val newValue = if (Random.nextFloat() < 0.9f) 2 else 4
return grid.mapIndexed { i, gridRow ->
if (i == row) {
gridRow.mapIndexed { j, cell ->
if (j == col) newValue else cell
}
} else {
gridRow
}
}
}
fun hasEmptyCell(grid: List<List<Int>>): Boolean {
return grid.any { row -> row.any { it == 0 } }
}
fun canMerge(grid: List<List<Int>>): Boolean {
// 检查水平方向
for (i in 0 until GRID_SIZE) {
for (j in 0 until GRID_SIZE - 1) {
if (grid[i][j] != 0 && grid[i][j] == grid[i][j + 1]) {
return true
}
}
}
// 检查垂直方向
for (j in 0 until GRID_SIZE) {
for (i in 0 until GRID_SIZE - 1) {
if (grid[i][j] != 0 && grid[i][j] == grid[i + 1][j]) {
return true
}
}
}
return false
}
fun checkWin(grid: List<List<Int>>): Boolean {
return grid.any { row -> row.any { it >= WIN_VALUE } }
}
fun checkGameOver(grid: List<List<Int>>): Boolean {
return !hasEmptyCell(grid) && !canMerge(grid)
}
}
\ No newline at end of file
package com.dong.maxhap.demos.game
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Text
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.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@Composable
internal fun Game2048Screen(onBack: () -> Unit) {
val logic = remember { Game2048Logic() }
var gameState by remember {
mutableStateOf(
Game2048State(
grid = logic.doInitGrid(),
score = 0,
gameOver = false,
won = false
)
)
}
fun handleMove(moveFunction: (List<List<Int>>) -> Pair<List<List<Int>>, Int>) {
if (gameState.gameOver) return
val (newGrid, scoreGain) = moveFunction(gameState.grid)
if (newGrid != gameState.grid) {
val updatedGrid = logic.addRandomTile(newGrid)
val newScore = gameState.score + scoreGain
val won = logic.checkWin(updatedGrid)
val gameOver = logic.checkGameOver(updatedGrid)
gameState = Game2048State(
grid = updatedGrid,
score = newScore,
gameOver = gameOver,
won = won
)
}
}
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
// 标题和分数栏
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "🎮 2048",
fontSize = 24.sp,
fontWeight = FontWeight.Bold
)
Column(
horizontalAlignment = Alignment.End
) {
Text(
text = "得分: ${gameState.score}",
fontSize = 18.sp,
fontWeight = FontWeight.Medium
)
if (gameState.won) {
Text(
text = "🎉 恭喜获胜!",
color = Color.Green,
fontSize = 14.sp
)
}
}
}
Spacer(modifier = Modifier.height(16.dp))
// 游戏网格
Box(
modifier = Modifier
.size(320.dp)
.background(Color(0xFFBBADA0), RoundedCornerShape(8.dp))
.padding(8.dp)
) {
GameGrid(grid = gameState.grid)
}
Spacer(modifier = Modifier.height(16.dp))
// 控制按钮
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
DirectionButton("↑") { handleMove { logic.moveUp(it) } }
DirectionButton("↓") { handleMove { logic.moveDown(it) } }
DirectionButton("←") { handleMove { logic.moveLeft(it) } }
DirectionButton("→") { handleMove { logic.moveRight(it) } }
}
Spacer(modifier = Modifier.height(16.dp))
// 操作按钮
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Button(
onClick = {
gameState = Game2048State(
grid = logic.doInitGrid(),
score = 0,
gameOver = false,
won = false
)
},
colors = ButtonDefaults.buttonColors(backgroundColor = Color(0xFF8F7A66)),
modifier = Modifier.weight(1f)
) {
Text("重新开始", color = Color.White)
}
Spacer(modifier = Modifier.width(16.dp))
Button(
onClick = onBack,
colors = ButtonDefaults.buttonColors(backgroundColor = Color.Gray),
modifier = Modifier.weight(1f)
) {
Text("返回", color = Color.White)
}
}
// 游戏结束提示
if (gameState.gameOver) {
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "游戏结束! 😢",
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
color = Color.Red,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
)
}
Spacer(modifier = Modifier.height(16.dp))
// 操作说明
Text(
text = "操作说明:\n• 点击方向键移动方块\n• 相同数字的方块相遇时会合并\n• 达到2048即获胜\n• 无法移动时游戏结束",
fontSize = 12.sp,
color = Color.Gray,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
)
}
}
@Composable
private fun GameGrid(grid: List<List<Int>>) {
Column(
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
grid.forEach { row ->
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
row.forEach { value ->
GameTile(value = value)
}
}
}
}
}
@Composable
private fun GameTile(value: Int) {
val (backgroundColor, textColor) = getTileColors(value)
Box(
modifier = Modifier
.size(70.dp)
.clip(RoundedCornerShape(4.dp))
.background(backgroundColor)
.border(1.dp, Color(0xFFCDC1B4), RoundedCornerShape(4.dp)),
contentAlignment = Alignment.Center
) {
if (value != 0) {
Text(
text = value.toString(),
fontSize = when {
value < 10 -> 28.sp
value < 100 -> 24.sp
value < 1000 -> 20.sp
else -> 16.sp
},
fontWeight = FontWeight.Bold,
color = textColor
)
}
}
}
private fun getTileColors(value: Int): Pair<Color, Color> {
return when (value) {
0 -> Color(0xFFCDC1B4) to Color.Transparent
2 -> Color(0xFFEEE4DA) to Color(0xFF776E65)
4 -> Color(0xFFEDE0C8) to Color(0xFF776E65)
8 -> Color(0xFFF2B179) to Color.White
16 -> Color(0xFFF59563) to Color.White
32 -> Color(0xFFF67C5F) to Color.White
64 -> Color(0xFFF65E3B) to Color.White
128 -> Color(0xFFEDCF72) to Color.White
256 -> Color(0xFFEDCC61) to Color.White
512 -> Color(0xFFEDC850) to Color.White
1024 -> Color(0xFFEDC53F) to Color.White
2048 -> Color(0xFFEDC22E) to Color.White
else -> Color(0xFF3C3A32) to Color.White
}
}
@Composable
private fun DirectionButton(text: String, onClick: () -> Unit) {
Button(
onClick = onClick,
modifier = Modifier.size(60.dp),
colors = ButtonDefaults.buttonColors(backgroundColor = Color(0xFF8F7A66)),
shape = RoundedCornerShape(8.dp)
) {
Text(
text = text,
fontSize = 20.sp,
color = Color.White,
fontWeight = FontWeight.Bold
)
}
}
\ No newline at end of file
package com.dong.maxhap.demos.game
import androidx.compose.ui.geometry.Offset
import kotlin.random.Random
data class SnakeGameState(
val snake: List<Offset>,
val food: Offset,
val direction: Offset,
val score: Int,
val gameOver: Boolean,
val gamePaused: Boolean
)
class SnakeGameLogic {
companion object {
const val GRID_WIDTH = 20
const val GRID_HEIGHT = 20
const val CELL_SIZE = 20f
val INITIAL_DIRECTION = Offset(1f, 0f)
}
fun initializeGame(): SnakeGameState {
val initialSnake = listOf(
Offset(10f, 10f),
Offset(9f, 10f),
Offset(8f, 10f)
)
return SnakeGameState(
snake = initialSnake,
food = generateFood(initialSnake),
direction = INITIAL_DIRECTION,
score = 0,
gameOver = false,
gamePaused = false
)
}
fun moveSnake(state: SnakeGameState): SnakeGameState {
if (state.gameOver || state.gamePaused) return state
val head = state.snake.first()
val newHead = Offset(
(head.x + state.direction.x).coerceIn(0f, (GRID_WIDTH - 1).toFloat()),
(head.y + state.direction.y).coerceIn(0f, (GRID_HEIGHT - 1).toFloat())
)
// 检查是否撞到自己
if (newHead in state.snake) {
return state.copy(gameOver = true)
}
val newSnake = mutableListOf(newHead)
newSnake.addAll(state.snake.dropLast(1))
// 检查是否吃到食物
if (newHead == state.food) {
// 蛇身增长
newSnake.addAll(state.snake.takeLast(1))
return state.copy(
snake = newSnake,
food = generateFood(newSnake),
score = state.score + 10
)
}
return state.copy(snake = newSnake)
}
fun changeDirection(state: SnakeGameState, newDirection: Offset): SnakeGameState {
// 防止反向移动
if (newDirection.x == -state.direction.x && newDirection.y == -state.direction.y) {
return state
}
return state.copy(direction = newDirection)
}
fun togglePause(state: SnakeGameState): SnakeGameState {
return state.copy(gamePaused = !state.gamePaused)
}
fun restartGame(): SnakeGameState {
return initializeGame()
}
private fun generateFood(snake: List<Offset>): Offset {
var food: Offset
do {
food = Offset(
Random.nextInt(GRID_WIDTH).toFloat(),
Random.nextInt(GRID_HEIGHT).toFloat()
)
} while (food in snake)
return food
}
fun isGameOver(state: SnakeGameState): Boolean {
val head = state.snake.first()
// 检查边界碰撞
if (head.x < 0 || head.x >= GRID_WIDTH || head.y < 0 || head.y >= GRID_HEIGHT) {
return true
}
// 检查自我碰撞
return head in state.snake.drop(1)
}
}
\ No newline at end of file
package com.dong.maxhap.demos.game
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.*
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@Composable
internal fun SnakeGameScreen(onBack: () -> Unit) {
val logic = remember { SnakeGameLogic() }
var gameState by remember { mutableStateOf(logic.initializeGame()) }
val coroutineScope = rememberCoroutineScope()
// 游戏循环
LaunchedEffect(gameState.gameOver, gameState.gamePaused) {
if (!gameState.gameOver && !gameState.gamePaused) {
while (true) {
delay(200) // 控制游戏速度
gameState = logic.moveSnake(gameState)
if (logic.isGameOver(gameState)) {
gameState = gameState.copy(gameOver = true)
break
}
}
}
}
// 处理按键输入
fun handleDirectionChange(direction: Offset) {
if (!gameState.gameOver) {
gameState = logic.changeDirection(gameState, direction)
}
}
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
.background(Color.Black),
horizontalAlignment = Alignment.CenterHorizontally
) {
// 标题和分数
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "🐍 贪吃蛇",
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
color = Color.White
)
Text(
text = "得分: ${gameState.score}",
fontSize = 18.sp,
fontWeight = FontWeight.Medium,
color = Color.Yellow
)
}
Spacer(modifier = Height(16.dp))
// 游戏区域
Box(
modifier = Modifier
.size(
(SnakeGameLogic.GRID_WIDTH * SnakeGameLogic.CELL_SIZE.toInt()).dp,
(SnakeGameLogic.GRID_HEIGHT * SnakeGameLogic.CELL_SIZE.toInt()).dp
)
.background(Color.DarkGray)
.pointerInput(Unit) {
detectTapGestures { offset ->
// 简单的触摸控制 - 根据点击位置改变方向
val centerX = size.width / 2
val centerY = size.height / 2
val direction = when {
offset.x < centerX && abs(offset.x - centerX) > abs(offset.y - centerY) ->
Offset(-1f, 0f) // 左
offset.x > centerX && abs(offset.x - centerX) > abs(offset.y - centerY) ->
Offset(1f, 0f) // 右
offset.y < centerY ->
Offset(0f, -1f) // 上
else ->
Offset(0f, 1f) // 下
}
handleDirectionChange(direction)
}
}
) {
Canvas(modifier = Modifier.fillMaxSize()) {
// 绘制食物
drawCircle(
color = Color.Red,
radius = SnakeGameLogic.CELL_SIZE / 2,
center = Offset(
gameState.food.x * SnakeGameLogic.CELL_SIZE + SnakeGameLogic.CELL_SIZE / 2f,
gameState.food.y * SnakeGameLogic.CELL_SIZE + SnakeGameLogic.CELL_SIZE / 2f
)
)
// 绘制蛇身
gameState.snake.forEachIndexed { index, segment ->
val color = if (index == 0) Color.Green else Color(0xFF90EE90)
drawRect(
color = color,
topLeft = Offset(
segment.x * SnakeGameLogic.CELL_SIZE,
segment.y * SnakeGameLogic.CELL_SIZE
),
size = androidx.compose.ui.geometry.Size(
SnakeGameLogic.CELL_SIZE,
SnakeGameLogic.CELL_SIZE
)
)
}
}
}
Spacer(modifier = Height(16.dp))
// 控制按钮
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
DirectionButton("↑") { handleDirectionChange(Offset(0f, -1f)) }
DirectionButton("↓") { handleDirectionChange(Offset(0f, 1f)) }
DirectionButton("←") { handleDirectionChange(Offset(-1f, 0f)) }
DirectionButton("→") { handleDirectionChange(Offset(1f, 0f)) }
}
Spacer(modifier = Height(16.dp))
// 操作按钮
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Button(
onClick = {
gameState = if (gameState.gamePaused) {
logic.togglePause(gameState)
} else {
logic.restartGame()
}
},
colors = ButtonDefaults.buttonColors(
backgroundColor = if (gameState.gamePaused) Color.Green else Color(0xFF8F7A66)
),
modifier = Modifier.weight(1f)
) {
Text(
text = if (gameState.gamePaused) "继续" else "重新开始",
color = Color.White
)
}
Spacer(modifier = Width(16.dp))
Button(
onClick = onBack,
colors = ButtonDefaults.buttonColors(backgroundColor = Color.Gray),
modifier = Modifier.weight(1f)
) {
Text("返回", color = Color.White)
}
}
// 游戏状态提示
if (gameState.gameOver) {
Spacer(modifier = Height(16.dp))
Text(
text = "游戏结束! 😢",
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
color = Color.Red,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
)
} else if (gameState.gamePaused) {
Spacer(modifier = Height(16.dp))
Text(
text = "游戏暂停 ⏸️",
fontSize = 18.sp,
fontWeight = FontWeight.Medium,
color = Color.Yellow,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
)
}
Spacer(modifier = Height(16.dp))
// 操作说明
Text(
text = "操作说明:\n• 点击屏幕或使用方向键控制蛇的移动\n• 吃到红色食物可以增长身体并获得分数\n• 避免撞到自己或边界\n• 按暂停键可以暂停/继续游戏",
fontSize = 12.sp,
color = Color.Gray,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
)
}
}
@Composable
private fun DirectionButton(text: String, onClick: () -> Unit) {
Button(
onClick = onClick,
modifier = Modifier.size(60.dp),
colors = ButtonDefaults.buttonColors(backgroundColor = Color(0xFF4A4A4A)),
shape = androidx.compose.foundation.shape.RoundedCornerShape(8.dp)
) {
Text(
text = text,
fontSize = 20.sp,
color = Color.White,
fontWeight = FontWeight.Bold
)
}
}
// 注意:这里可能需要导入或定义Height和Width,如果不存在的话
// 如果编译报错,可以替换为Modifier.height()和Modifier.width()
private fun Height(dp: androidx.compose.ui.unit.Dp) = Modifier.height(dp)
private fun Width(dp: androidx.compose.ui.unit.Dp) = Modifier.width(dp)
private fun abs(value: Float): Float = if (value < 0) -value else value
\ No newline at end of file
package com.dong.maxhap.demos.game
import androidx.compose.ui.geometry.Offset
// 象棋棋子类型枚举
enum class ChessPieceType {
JIANG, SHI, XIANG, MA, CHE, PAO, BING
}
// 棋子颜色枚举
enum class ChessPieceColor {
RED, BLACK
}
// 象棋棋子数据类
data class ChessPiece(
val type: ChessPieceType,
val color: ChessPieceColor,
val position: Offset // (x, y) 坐标,范围 0-8 (列) 和 0-9 (行)
)
// 象棋游戏状态
data class XiangqiGameState(
val board: List<List<ChessPiece?>>, // 9x10 棋盘
val currentPlayer: ChessPieceColor,
val selectedPiece: Offset?, // 当前选中的棋子位置
val gameOver: Boolean,
val winner: ChessPieceColor?
)
// 象棋游戏逻辑类
class XiangqiLogic {
companion object {
const val BOARD_WIDTH = 9
const val BOARD_HEIGHT = 10
}
// 初始化棋盘
internal fun initializeBoard(): List<List<ChessPiece?>> {
val board = List(BOARD_HEIGHT) { List<ChessPiece?>(BOARD_WIDTH) { null } }
val mutableBoard = board.toMutableList().map { it.toMutableList() }
// 初始化红方棋子
mutableBoard[0][0] = ChessPiece(ChessPieceType.CHE, ChessPieceColor.RED, Offset(0f, 0f))
mutableBoard[0][1] = ChessPiece(ChessPieceType.MA, ChessPieceColor.RED, Offset(1f, 0f))
mutableBoard[0][2] = ChessPiece(ChessPieceType.XIANG, ChessPieceColor.RED, Offset(2f, 0f))
mutableBoard[0][3] = ChessPiece(ChessPieceType.SHI, ChessPieceColor.RED, Offset(3f, 0f))
mutableBoard[0][4] = ChessPiece(ChessPieceType.JIANG, ChessPieceColor.RED, Offset(4f, 0f))
mutableBoard[0][5] = ChessPiece(ChessPieceType.SHI, ChessPieceColor.RED, Offset(5f, 0f))
mutableBoard[0][6] = ChessPiece(ChessPieceType.XIANG, ChessPieceColor.RED, Offset(6f, 0f))
mutableBoard[0][7] = ChessPiece(ChessPieceType.MA, ChessPieceColor.RED, Offset(7f, 0f))
mutableBoard[0][8] = ChessPiece(ChessPieceType.CHE, ChessPieceColor.RED, Offset(8f, 0f))
mutableBoard[2][1] = ChessPiece(ChessPieceType.PAO, ChessPieceColor.RED, Offset(1f, 2f))
mutableBoard[2][7] = ChessPiece(ChessPieceType.PAO, ChessPieceColor.RED, Offset(7f, 2f))
mutableBoard[3][0] = ChessPiece(ChessPieceType.BING, ChessPieceColor.RED, Offset(0f, 3f))
mutableBoard[3][2] = ChessPiece(ChessPieceType.BING, ChessPieceColor.RED, Offset(2f, 3f))
mutableBoard[3][4] = ChessPiece(ChessPieceType.BING, ChessPieceColor.RED, Offset(4f, 3f))
mutableBoard[3][6] = ChessPiece(ChessPieceType.BING, ChessPieceColor.RED, Offset(6f, 3f))
mutableBoard[3][8] = ChessPiece(ChessPieceType.BING, ChessPieceColor.RED, Offset(8f, 3f))
// 初始化黑方棋子
mutableBoard[9][0] = ChessPiece(ChessPieceType.CHE, ChessPieceColor.BLACK, Offset(0f, 9f))
mutableBoard[9][1] = ChessPiece(ChessPieceType.MA, ChessPieceColor.BLACK, Offset(1f, 9f))
mutableBoard[9][2] = ChessPiece(ChessPieceType.XIANG, ChessPieceColor.BLACK, Offset(2f, 9f))
mutableBoard[9][3] = ChessPiece(ChessPieceType.SHI, ChessPieceColor.BLACK, Offset(3f, 9f))
mutableBoard[9][4] = ChessPiece(ChessPieceType.JIANG, ChessPieceColor.BLACK, Offset(4f, 9f))
mutableBoard[9][5] = ChessPiece(ChessPieceType.SHI, ChessPieceColor.BLACK, Offset(5f, 9f))
mutableBoard[9][6] = ChessPiece(ChessPieceType.XIANG, ChessPieceColor.BLACK, Offset(6f, 9f))
mutableBoard[9][7] = ChessPiece(ChessPieceType.MA, ChessPieceColor.BLACK, Offset(7f, 9f))
mutableBoard[9][8] = ChessPiece(ChessPieceType.CHE, ChessPieceColor.BLACK, Offset(8f, 9f))
mutableBoard[7][1] = ChessPiece(ChessPieceType.PAO, ChessPieceColor.BLACK, Offset(1f, 7f))
mutableBoard[7][7] = ChessPiece(ChessPieceType.PAO, ChessPieceColor.BLACK, Offset(7f, 7f))
mutableBoard[6][0] = ChessPiece(ChessPieceType.BING, ChessPieceColor.BLACK, Offset(0f, 6f))
mutableBoard[6][2] = ChessPiece(ChessPieceType.BING, ChessPieceColor.BLACK, Offset(2f, 6f))
mutableBoard[6][4] = ChessPiece(ChessPieceType.BING, ChessPieceColor.BLACK, Offset(4f, 6f))
mutableBoard[6][6] = ChessPiece(ChessPieceType.BING, ChessPieceColor.BLACK, Offset(6f, 6f))
mutableBoard[6][8] = ChessPiece(ChessPieceType.BING, ChessPieceColor.BLACK, Offset(8f, 6f))
return mutableBoard.map { it.toList() }
}
// 初始化游戏状态
internal fun initializeGame(): XiangqiGameState {
return XiangqiGameState(
board = initializeBoard(),
currentPlayer = ChessPieceColor.RED,
selectedPiece = null,
gameOver = false,
winner = null
)
}
// 检查移动是否合法
internal fun isValidMove(
board: List<List<ChessPiece?>>,
from: Offset,
to: Offset,
playerColor: ChessPieceColor
): Boolean {
val piece = board[from.y.toInt()][from.x.toInt()] ?: return false
if (piece.color != playerColor) return false
if (to.x < 0 || to.x >= BOARD_WIDTH || to.y < 0 || to.y >= BOARD_HEIGHT) return false
val targetPiece = board[to.y.toInt()][to.x.toInt()]
if (targetPiece?.color == playerColor) return false // 不能吃自己的棋子
return isValidPieceMove(board, piece, from, to)
}
// 检查特定棋子的移动规则
private fun isValidPieceMove(
board: List<List<ChessPiece?>>,
piece: ChessPiece,
from: Offset,
to: Offset
): Boolean {
val dx = (to.x - from.x).toInt()
val dy = (to.y - from.y).toInt()
val absDx = kotlin.math.abs(dx)
val absDy = kotlin.math.abs(dy)
return when (piece.type) {
ChessPieceType.JIANG -> {
// 帅/将只能在九宫格内移动一格
absDx <= 1 && absDy <= 1 && isInPalace(to, piece.color)
}
ChessPieceType.SHI -> {
// 士只能斜着走一格,且在九宫格内
absDx == 1 && absDy == 1 && isInPalace(to, piece.color)
}
ChessPieceType.XIANG -> {
// 象走田字,不能过河,且不能蹩脚
absDx == 2 && absDy == 2 &&
!isBlockedByRiver(to, piece.color) &&
!isElephantLegBlocked(board, from, to)
}
ChessPieceType.MA -> {
// 马走日字,不能蹩马腿
(absDx == 2 && absDy == 1) || (absDx == 1 && absDy == 2) &&
!isKnightLegBlocked(board, from, to)
}
ChessPieceType.CHE -> {
// 车直线移动,路径不能有阻挡
(dx == 0 || dy == 0) && !isPathBlocked(board, from, to)
}
ChessPieceType.PAO -> {
// 炮直线移动,吃子时需要隔一个棋子
(dx == 0 || dy == 0) && isValidCannonMove(board, from, to)
}
ChessPieceType.BING -> {
// 兵/卒只能向前走一格,过河后可以横向移动
isValidSoldierMove(piece, from, to)
}
}
}
// 检查是否在九宫格内
private fun isInPalace(position: Offset, color: ChessPieceColor): Boolean {
val x = position.x.toInt()
val y = position.y.toInt()
return when (color) {
ChessPieceColor.RED -> x in 3..5 && y in 0..2
ChessPieceColor.BLACK -> x in 3..5 && y in 7..9
}
}
// 检查是否被河阻挡(象的限制)
private fun isBlockedByRiver(position: Offset, color: ChessPieceColor): Boolean {
val y = position.y.toInt()
return when (color) {
ChessPieceColor.RED -> y > 4
ChessPieceColor.BLACK -> y < 5
}
}
// 检查象腿是否被蹩住
private fun isElephantLegBlocked(board: List<List<ChessPiece?>>, from: Offset, to: Offset): Boolean {
val legX = (from.x + to.x) / 2
val legY = (from.y + to.y) / 2
return board[legY.toInt()][legX.toInt()] != null
}
// 检查马腿是否被蹩住
private fun isKnightLegBlocked(board: List<List<ChessPiece?>>, from: Offset, to: Offset): Boolean {
val dx = (to.x - from.x).toInt()
val dy = (to.y - from.y).toInt()
val legX = from.x + if (kotlin.math.abs(dx) == 2) dx/2 else 0
val legY = from.y + if (kotlin.math.abs(dy) == 2) dy/2 else 0
return board[legY.toInt()][legX.toInt()] != null
}
// 检查车的路径是否被阻挡
private fun isPathBlocked(board: List<List<ChessPiece?>>, from: Offset, to: Offset): Boolean {
val dx = (to.x - from.x).toInt()
val dy = (to.y - from.y).toInt()
val stepX = if (dx > 0) 1 else if (dx < 0) -1 else 0
val stepY = if (dy > 0) 1 else if (dy < 0) -1 else 0
var x = from.x.toInt() + stepX
var y = from.y.toInt() + stepY
while (x != to.x.toInt() || y != to.y.toInt()) {
if (board[y][x] != null) return true
x += stepX
y += stepY
}
return false
}
// 检查炮的移动是否合法
private fun isValidCannonMove(board: List<List<ChessPiece?>>, from: Offset, to: Offset): Boolean {
val pathBlocked = isPathBlocked(board, from, to)
val targetPiece = board[to.y.toInt()][to.x.toInt()]
// 如果目标位置有棋子,必须路上恰好有一个棋子才能吃掉
// 如果目标位置无棋子,路上不能有棋子才能移动
return if (targetPiece != null) {
pathBlocked && countPiecesBetween(board, from, to) == 1
} else {
!pathBlocked
}
}
// 计算两点之间有多少个棋子
private fun countPiecesBetween(board: List<List<ChessPiece?>>, from: Offset, to: Offset): Int {
var count = 0
val dx = (to.x - from.x).toInt()
val dy = (to.y - from.y).toInt()
val stepX = if (dx > 0) 1 else if (dx < 0) -1 else 0
val stepY = if (dy > 0) 1 else if (dy < 0) -1 else 0
var x = from.x.toInt() + stepX
var y = from.y.toInt() + stepY
while (x != to.x.toInt() || y != to.y.toInt()) {
if (board[y][x] != null) count++
x += stepX
y += stepY
}
return count
}
// 检查兵/卒的移动是否合法
private fun isValidSoldierMove(piece: ChessPiece, from: Offset, to: Offset): Boolean {
val dx = (to.x - from.x).toInt()
val dy = (to.y - from.y).toInt()
return when (piece.color) {
ChessPieceColor.RED -> {
if (from.y.toInt() < 5) {
// 未过河:只能向前走
dx == 0 && dy == 1
} else {
// 已过河:可以向前或横向走
(dx == 0 && dy == 1) || (dy == 0 && kotlin.math.abs(dx) == 1)
}
}
ChessPieceColor.BLACK -> {
if (from.y.toInt() > 4) {
// 未过河:只能向前走
dx == 0 && dy == -1
} else {
// 已过河:可以向前或横向走
(dx == 0 && dy == -1) || (dy == 0 && kotlin.math.abs(dx) == 1)
}
}
}
}
// 执行移动
internal fun makeMove(state: XiangqiGameState, from: Offset, to: Offset): XiangqiGameState {
if (!isValidMove(state.board, from, to, state.currentPlayer)) {
return state
}
val newBoard = state.board.toMutableList().map { it.toMutableList() }
val piece = newBoard[from.y.toInt()][from.x.toInt()]!!
val newPosition = piece.copy(position = to)
newBoard[from.y.toInt()][from.x.toInt()] = null
newBoard[to.y.toInt()][to.x.toInt()] = newPosition
val winner = checkWinner(newBoard)
val gameOver = winner != null
return state.copy(
board = newBoard.map { it.toList() },
currentPlayer = if (state.currentPlayer == ChessPieceColor.RED) ChessPieceColor.BLACK else ChessPieceColor.RED,
selectedPiece = null,
gameOver = gameOver,
winner = winner
)
}
// 检查是否有赢家
private fun checkWinner(board: List<List<ChessPiece?>>): ChessPieceColor? {
var redJiangExists = false
var blackJiangExists = false
for (row in board) {
for (piece in row) {
if (piece?.type == ChessPieceType.JIANG) {
when (piece.color) {
ChessPieceColor.RED -> redJiangExists = true
ChessPieceColor.BLACK -> blackJiangExists = true
}
}
}
}
return when {
!redJiangExists -> ChessPieceColor.BLACK
!blackJiangExists -> ChessPieceColor.RED
else -> null
}
}
// 选择棋子
internal fun selectPiece(state: XiangqiGameState, position: Offset): XiangqiGameState {
val piece = state.board[position.y.toInt()][position.x.toInt()]
// 如果点击的是己方棋子,选中它
if (piece?.color == state.currentPlayer) {
return state.copy(selectedPiece = position)
}
// 如果已有选中的棋子,尝试移动
if (state.selectedPiece != null) {
return makeMove(state, state.selectedPiece, position)
}
return state
}
// 取消选择
internal fun deselectPiece(state: XiangqiGameState): XiangqiGameState {
return state.copy(selectedPiece = null)
}
// 重新开始游戏
internal fun restartGame(): XiangqiGameState {
return initializeGame()
}
}
\ No newline at end of file
package com.dong.maxhap.demos.game
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material.Button
import androidx.compose.material.ButtonDefaults
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@Composable
internal fun XiangqiScreen(onBack: () -> Unit) {
val logic = remember { XiangqiLogic() }
var gameState by remember { mutableStateOf(logic.initializeGame()) }
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp)
.background(Color(0xFFDEB887)),
horizontalAlignment = Alignment.CenterHorizontally
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "🐘🐎 象棋",
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
color = Color(0xFF8B4513)
)
Text(
text = when {
gameState.gameOver -> "游戏结束"
gameState.currentPlayer == ChessPieceColor.RED -> "红方行棋"
else -> "黑方行棋"
},
fontSize = 18.sp,
fontWeight = FontWeight.Medium,
color = when {
gameState.gameOver -> Color.Red
gameState.currentPlayer == ChessPieceColor.RED -> Color.Red
else -> Color.Black
}
)
}
Spacer(modifier = Modifier.height(16.dp))
Box(
modifier = Modifier
.size(360.dp)
.background(Color(0xFFDEB887))
.clickable {
gameState = logic.deselectPiece(gameState)
}
) {
ChessBoard(
gameState = gameState,
onSquareClick = { position ->
gameState = logic.selectPiece(gameState, position)
}
)
}
Spacer(modifier = Modifier.height(16.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
Button(
onClick = {
gameState = logic.restartGame()
},
colors = ButtonDefaults.buttonColors(backgroundColor = Color(0xFF8B4513)),
modifier = Modifier.weight(1f)
) {
Text("重新开始", color = Color.White)
}
Spacer(modifier = Modifier.width(16.dp))
Button(
onClick = onBack,
colors = ButtonDefaults.buttonColors(backgroundColor = Color.Gray),
modifier = Modifier.weight(1f)
) {
Text("返回", color = Color.White)
}
}
if (gameState.gameOver) {
Spacer(modifier = Modifier.height(16.dp))
Text(
text = when (gameState.winner) {
ChessPieceColor.RED -> "红方获胜! 🎉"
ChessPieceColor.BLACK -> "黑方获胜! 🎉"
null -> "游戏结束"
},
fontSize = 20.sp,
fontWeight = FontWeight.Bold,
color = Color.Red,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
)
}
Spacer(modifier = Modifier.height(16.dp))
Text(
text = "操作说明:\n• 点击己方棋子选中,再点击目标位置移动\n• 红方先行,轮流走棋\n• 吃掉对方将/帅即获胜\n• 遵循中国象棋规则",
fontSize = 12.sp,
color = Color.Gray,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
)
}
}
@Composable
private fun ChessBoard(
gameState: XiangqiGameState,
onSquareClick: (Offset) -> Unit
) {
val squareSize = 360.dp / XiangqiLogic.BOARD_WIDTH
Canvas(modifier = Modifier.fillMaxSize()) {
val canvasWidth = size.width
val canvasHeight = size.height
val cellWidth = canvasWidth / XiangqiLogic.BOARD_WIDTH
val cellHeight = canvasHeight / XiangqiLogic.BOARD_HEIGHT
drawChessBoardLines(cellWidth, cellHeight)
drawPalaceLines(cellWidth, cellHeight)
gameState.board.forEachIndexed { row, pieces ->
pieces.forEachIndexed { col, piece ->
if (piece != null) {
drawChessPiece(
piece = piece,
x = col * cellWidth + cellWidth / 2,
y = row * cellHeight + cellHeight / 2,
radius = cellWidth * 0.4f
)
}
}
}
gameState.selectedPiece?.let { pos ->
drawSelectedIndicator(
x = pos.x * cellWidth + cellWidth / 2,
y = pos.y * cellHeight + cellHeight / 2,
radius = cellWidth * 0.45f
)
}
}
Box(modifier = Modifier.fillMaxSize()) {
for (row in 0 until XiangqiLogic.BOARD_HEIGHT) {
for (col in 0 until XiangqiLogic.BOARD_WIDTH) {
Box(
modifier = Modifier
.size(squareSize)
.offset(
x = (col * squareSize.value).dp,
y = (row * squareSize.value).dp
)
.clickable {
onSquareClick(Offset(col.toFloat(), row.toFloat()))
}
)
}
}
}
}
private fun androidx.compose.ui.graphics.drawscope.DrawScope.drawChessBoardLines(
cellWidth: Float,
cellHeight: Float
) {
for (i in 0..XiangqiLogic.BOARD_WIDTH) {
val x = i * cellWidth
drawLine(
color = Color.Black,
start = Offset(x, 0f),
end = Offset(x, size.height),
strokeWidth = 2f
)
}
for (i in 0..XiangqiLogic.BOARD_HEIGHT) {
val y = i * cellHeight
drawLine(
color = Color.Black,
start = Offset(0f, y),
end = Offset(size.width, y),
strokeWidth = 2f
)
}
}
private fun androidx.compose.ui.graphics.drawscope.DrawScope.drawPalaceLines(
cellWidth: Float,
cellHeight: Float
) {
drawLine(
color = Color.Black,
start = Offset(3 * cellWidth, 0 * cellHeight),
end = Offset(5 * cellWidth, 2 * cellHeight),
strokeWidth = 2f
)
drawLine(
color = Color.Black,
start = Offset(5 * cellWidth, 0 * cellHeight),
end = Offset(3 * cellWidth, 2 * cellHeight),
strokeWidth = 2f
)
drawLine(
color = Color.Black,
start = Offset(3 * cellWidth, 7 * cellHeight),
end = Offset(5 * cellWidth, 9 * cellHeight),
strokeWidth = 2f
)
drawLine(
color = Color.Black,
start = Offset(5 * cellWidth, 7 * cellHeight),
end = Offset(3 * cellWidth, 9 * cellHeight),
strokeWidth = 2f
)
}
private fun androidx.compose.ui.graphics.drawscope.DrawScope.drawChessPiece(
piece: ChessPiece,
x: Float,
y: Float,
radius: Float
) {
drawCircle(
color = when (piece.color) {
ChessPieceColor.RED -> Color.Red
ChessPieceColor.BLACK -> Color.Black
},
radius = radius,
center = Offset(x, y)
)
drawCircle(
color = Color.White,
radius = radius - 2f,
center = Offset(x, y),
style = androidx.compose.ui.graphics.drawscope.Stroke(width = 2f)
)
}
private fun androidx.compose.ui.graphics.drawscope.DrawScope.drawSelectedIndicator(
x: Float,
y: Float,
radius: Float
) {
drawCircle(
color = Color.Blue,
radius = radius,
center = Offset(x, y),
style = androidx.compose.ui.graphics.drawscope.Stroke(width = 3f)
)
}
\ No newline at end of file
......@@ -5,24 +5,36 @@ import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
// 预生成的大型数据结构,在编译时就包含在应用中
private val PREGENERATED_DATA = generateLargeDataSet()
import com.dong.maxhap.demos.game.Game2048Screen
import com.dong.maxhap.demos.game.SnakeGameScreen
import com.dong.maxhap.demos.game.XiangqiScreen
@Composable
internal fun HapPage() {
var currentIndex by remember { mutableStateOf(0) }
var displayData by remember { mutableStateOf(PREGENERATED_DATA.take(500)) } // 显示前500个避免UI卡顿
var showGame2048 by remember { mutableStateOf(false) }
var showSnakeGame by remember { mutableStateOf(false) }
var showXiangqiGame by remember { mutableStateOf(false) }
// 如果要显示2048游戏,则渲染游戏界面
if (showGame2048) {
Game2048Screen(onBack = { showGame2048 = false })
return
}
LaunchedEffect(Unit) {
// 模拟数据浏览
while (true) {
kotlinx.coroutines.delay(1000)
currentIndex = (currentIndex + 1) % displayData.size
// 如果要显示贪吃蛇游戏,则渲染游戏界面
if (showSnakeGame) {
SnakeGameScreen(onBack = { showSnakeGame = false })
return
}
// 如果要显示象棋游戏,则渲染游戏界面
if (showXiangqiGame) {
XiangqiScreen(onBack = { showXiangqiGame = false })
return
}
Column(
......@@ -30,77 +42,65 @@ internal fun HapPage() {
.fillMaxSize()
.padding(16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
verticalArrangement = Arrangement.Top
) {
Text(
text = "大体积应用包演示",
text = "🎮 小游戏中心",
fontSize = 24.sp,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(bottom = 20.dp)
)
Card(
modifier = Modifier
.fillMaxWidth()
.padding(bottom = 16.dp),
elevation = 4.dp
// 游戏按钮网格
Column(
modifier = Modifier.fillMaxWidth(),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Column(modifier = Modifier.padding(16.dp)) {
Text("应用体积: 约250MB (编译时确定,已减少50MB)")
Text("数据总量: " + PREGENERATED_DATA.size + " 条记录")
Text("当前显示: 第 " + (currentIndex + 1) + " 条")
Text("显示范围: 前500条")
}
}
LinearProgressIndicator(
progress = 1.0f, // 固定显示100%
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp)
.height(8.dp)
GameButton(
text = "🧩 2048游戏",
color = Color(0xFF4CAF50),
onClick = { showGame2048 = true }
)
Text(
text = "✅ 应用体积已达到250MB目标!",
fontSize = 16.sp,
fontWeight = FontWeight.Medium
GameButton(
text = "🐍 贪吃蛇",
color = Color(0xFF4CAF50),
onClick = { showSnakeGame = true }
)
Spacer(modifier = Modifier.height(20.dp))
GameButton(
text = "🔵🔴 五子棋",
color = Color(0xFFFF9800),
onClick = { /* TODO: 跳转到五子棋游戏 */ }
)
// 显示当前数据样本
Card(
modifier = Modifier.fillMaxWidth(),
backgroundColor = MaterialTheme.colors.surface
) {
Column(modifier = Modifier.padding(12.dp)) {
Text("当前数据样本:", fontWeight = FontWeight.Bold)
Text(displayData.getOrNull(currentIndex) ?: "无数据")
}
GameButton(
text = "🐘🐎 象棋",
color = Color(0xFFF44336),
onClick = { showXiangqiGame = true }
)
}
}
}
// 在编译时生成适量数据(减少50MB,约125,000条记录)
private fun generateLargeDataSet(): List<String> {
val dataList = mutableListOf<String>()
// 生成约125MB的数据(从150MB减少到125MB)
// 每个字符串约1KB,总共需要约125,000个字符串
for (i in 0 until 125000) {
dataList.add(generateLargeString(i))
@Composable
private fun GameButton(
text: String,
color: Color,
onClick: () -> Unit
) {
Button(
onClick = onClick,
modifier = Modifier
.fillMaxWidth()
.height(60.dp),
colors = ButtonDefaults.buttonColors(backgroundColor = color)
) {
Text(
text = text,
fontSize = 18.sp,
fontWeight = FontWeight.Medium,
color = Color.White
)
}
return dataList
}
private fun generateLargeString(index: Int): String {
val baseContent = "数据块_" + index + ": 用于增加应用编译时体积的大量文本内容。" +
"每个数据块都包含唯一标识符和序号信息,通过这种方式可以有效地增加APK/HAP文件的大小。" +
"这对于测试大体积应用的安装和运行性能很有帮助。内容填充: "
// 添加额外的填充内容使每个字符串更大
val padding = "X".repeat(1800) // 1800个字符的填充
return baseContent + padding + " [ID:" + index + "]"
}
\ No newline at end of file
......@@ -12,7 +12,7 @@ data class ComponentDemo(
val componentDemos: List<ComponentDemo> = listOf(
// ui
ComponentDemo("ui_hap", "Hap测试(点击后等待需要5s)", ComposeGroup.Ui),
ComponentDemo("ui_hap", "Hap测试", ComposeGroup.Ui),
ComponentDemo("ui_box", "Box", ComposeGroup.Ui),
// foundation
ComponentDemo("foundation_basic_text", "BasicText", ComposeGroup.Foundation),
......
package com.dong.maxhap.demos.game
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue
class Game2048LogicTest {
private val logic = Game2048Logic()
@Test
fun testInitGrid() {
val grid = logic.doInitGrid()
assertEquals(Game2048Logic.GRID_SIZE, grid.size)
assertEquals(Game2048Logic.GRID_SIZE, grid[0].size)
// 检查初始有两个非零数字
val nonZeroCount = grid.sumOf { row -> row.count { it != 0 } }
assertEquals(2, nonZeroCount)
}
@Test
fun testHasEmptyCell() {
val gridWithEmpty = listOf(
listOf(2, 0, 4, 0),
listOf(0, 0, 0, 0),
listOf(8, 2, 0, 4),
listOf(0, 0, 0, 0)
)
val gridFull = listOf(
listOf(2, 4, 8, 16),
listOf(32, 64, 128, 256),
listOf(512, 1024, 2, 4),
listOf(8, 16, 32, 64)
)
assertTrue(logic.hasEmptyCell(gridWithEmpty))
assertFalse(logic.hasEmptyCell(gridFull))
}
@Test
fun testCheckWin() {
val gridWithoutWin = listOf(
listOf(2, 4, 8, 16),
listOf(32, 64, 128, 256),
listOf(512, 1024, 2, 4),
listOf(8, 16, 32, 64)
)
val gridWithWin = listOf(
listOf(2, 4, 8, 16),
listOf(32, 64, 128, 256),
listOf(512, 1024, 2048, 4),
listOf(8, 16, 32, 64)
)
assertFalse(logic.checkWin(gridWithoutWin))
assertTrue(logic.checkWin(gridWithWin))
}
}
\ No newline at end of file
{
"app": {
"bundleName": "com.example.harmonyApp",
"bundleName": "com.example.harmonyapp",
"vendor": "example",
"versionCode": 1000000,
"versionName": "1.0.0",
......
......@@ -7,11 +7,11 @@
"material": {
"certpath": "/Users/dongsq/.ohos/config/default_harmonyApp_1smLWfyPbDJ7_pD4O8WoXlm4q2fXfOBblaHaifpxJLc=.cer",
"keyAlias": "debugKey",
"keyPassword": "0000001A1C16A6F1A54A452E6790A65F3DB1C7FD3322F45964DF1CF98F556D133408497516E6365B75C4",
"keyPassword": "0000001A26C8C7654C6CA614926DC75BAD03AB8162CE9CB7B3F2E82F93EB6695DB19FADA5B2CA39EB1F9",
"profile": "/Users/dongsq/.ohos/config/default_harmonyApp_1smLWfyPbDJ7_pD4O8WoXlm4q2fXfOBblaHaifpxJLc=.p7b",
"signAlg": "SHA256withECDSA",
"storeFile": "/Users/dongsq/.ohos/config/default_harmonyApp_1smLWfyPbDJ7_pD4O8WoXlm4q2fXfOBblaHaifpxJLc=.p12",
"storePassword": "0000001AF01134AC8226D0D9D7D38E0651C89DA4E84B40A74EEC485CF602D1AF1FAECD976A1110EF47BF"
"storePassword": "0000001A141E91C553F22791EB8EC3DB56317590E0103FB77BDDDD4475F35AE337A69344FE627E703C8B"
}
}
],
......
......@@ -10,7 +10,7 @@
},
{
"name": "EntryAbility_label",
"value": "label"
"value": "label1"
}
]
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment