Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
K
KMPMaxHap
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
dsq
KMPMaxHap
Commits
b6616e1f
Commit
b6616e1f
authored
Feb 12, 2026
by
dsq
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
300
parent
8e022c00
Changes
29
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
29 changed files
with
1788 additions
and
897 deletions
+1788
-897
build.gradle.kts
composeApp/build.gradle.kts
+34
-0
1.JPG
composeApp/src/commonMain/composeResources/drawable/1.JPG
+0
-0
hap.kt
...pp/src/commonMain/kotlin/com.dong.maxhap/demos/Hap/hap.kt
+4
-0
LargeData1.kt
...src/commonMain/kotlin/com.dong.maxhap/demos/LargeData1.kt
+0
-55
LargeData2.kt
...src/commonMain/kotlin/com.dong.maxhap/demos/LargeData2.kt
+0
-57
LargeData3.kt
...src/commonMain/kotlin/com.dong.maxhap/demos/LargeData3.kt
+0
-54
LargeData4.kt
...src/commonMain/kotlin/com.dong.maxhap/demos/LargeData4.kt
+0
-50
LargeData5.kt
...src/commonMain/kotlin/com.dong.maxhap/demos/LargeData5.kt
+0
-47
LargeData6.kt
...src/commonMain/kotlin/com.dong.maxhap/demos/LargeData6.kt
+0
-47
LargeData7.kt
...src/commonMain/kotlin/com.dong.maxhap/demos/LargeData7.kt
+0
-50
LargeData8.kt
...src/commonMain/kotlin/com.dong.maxhap/demos/LargeData8.kt
+0
-47
Game2048Logic.kt
...onMain/kotlin/com.dong.maxhap/demos/game/Game2048Logic.kt
+137
-0
Game2048Screen.kt
...nMain/kotlin/com.dong.maxhap/demos/game/Game2048Screen.kt
+251
-0
SnakeGameLogic.kt
...nMain/kotlin/com.dong.maxhap/demos/game/SnakeGameLogic.kt
+107
-0
SnakeGameScreen.kt
...Main/kotlin/com.dong.maxhap/demos/game/SnakeGameScreen.kt
+245
-0
XiangqiLogic.kt
...monMain/kotlin/com.dong.maxhap/demos/game/XiangqiLogic.kt
+353
-0
XiangqiScreen.kt
...onMain/kotlin/com.dong.maxhap/demos/game/XiangqiScreen.kt
+280
-0
HapPage.kt
...c/commonMain/kotlin/com.dong.maxhap/demos/test/HapPage.kt
+70
-70
Navigation.kt
...ommonMain/kotlin/com.dong.maxhap/navigation/Navigation.kt
+1
-1
Game2048LogicTest.kt
...st/kotlin/com.dong.maxhap/demos/game/Game2048LogicTest.kt
+62
-0
app.json5
harmonyApp/AppScope/app.json5
+1
-1
build-profile.json5
harmonyApp/build-profile.json5
+2
-2
libkn.so
harmonyApp/entry/libs/arm64-v8a/libkn.so
+0
-0
libkn_api.h
harmonyApp/entry/src/main/cpp/include/libkn_api.h
+240
-415
string.json
harmonyApp/entry/src/main/resources/base/element/string.json
+1
-1
additional_asset_1.dat
...ntry/src/main/resources/base/media/additional_asset_1.dat
+0
-0
additional_asset_2.dat
...ntry/src/main/resources/base/media/additional_asset_2.dat
+0
-0
large_asset_1.dat
...App/entry/src/main/resources/base/media/large_asset_1.dat
+0
-0
large_asset_4.dat
...App/entry/src/main/resources/base/media/large_asset_4.dat
+0
-0
No files found.
composeApp/build.gradle.kts
View file @
b6616e1f
...
...
@@ -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
composeApp/src/commonMain/composeResources/drawable/1.JPG
0 → 100644
View file @
b6616e1f
This diff is collapsed.
Click to expand it.
composeApp/src/commonMain/kotlin/com.dong.maxhap/demos/Hap/hap.kt
0 → 100644
View file @
b6616e1f
package
com.dong.maxhap.demos.Hap
class
hap
{
}
\ No newline at end of file
composeApp/src/commonMain/kotlin/com.dong.maxhap/demos/LargeData1.kt
deleted
100644 → 0
View file @
8e022c00
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
composeApp/src/commonMain/kotlin/com.dong.maxhap/demos/LargeData2.kt
deleted
100644 → 0
View file @
8e022c00
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
composeApp/src/commonMain/kotlin/com.dong.maxhap/demos/LargeData3.kt
deleted
100644 → 0
View file @
8e022c00
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
composeApp/src/commonMain/kotlin/com.dong.maxhap/demos/LargeData4.kt
deleted
100644 → 0
View file @
8e022c00
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
composeApp/src/commonMain/kotlin/com.dong.maxhap/demos/LargeData5.kt
deleted
100644 → 0
View file @
8e022c00
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
composeApp/src/commonMain/kotlin/com.dong.maxhap/demos/LargeData6.kt
deleted
100644 → 0
View file @
8e022c00
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
composeApp/src/commonMain/kotlin/com.dong.maxhap/demos/LargeData7.kt
deleted
100644 → 0
View file @
8e022c00
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
composeApp/src/commonMain/kotlin/com.dong.maxhap/demos/LargeData8.kt
deleted
100644 → 0
View file @
8e022c00
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
composeApp/src/commonMain/kotlin/com.dong.maxhap/demos/game/Game2048Logic.kt
0 → 100644
View file @
b6616e1f
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
composeApp/src/commonMain/kotlin/com.dong.maxhap/demos/game/Game2048Screen.kt
0 → 100644
View file @
b6616e1f
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
(
0
xFFBBADA0
),
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
(
0
xFF8F7A66
)),
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
(
0
xFFCDC1B4
),
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
(
0
xFFCDC1B4
)
to
Color
.
Transparent
2
->
Color
(
0
xFFEEE4DA
)
to
Color
(
0
xFF776E65
)
4
->
Color
(
0
xFFEDE0C8
)
to
Color
(
0
xFF776E65
)
8
->
Color
(
0
xFFF2B179
)
to
Color
.
White
16
->
Color
(
0
xFFF59563
)
to
Color
.
White
32
->
Color
(
0
xFFF67C5F
)
to
Color
.
White
64
->
Color
(
0
xFFF65E3B
)
to
Color
.
White
128
->
Color
(
0
xFFEDCF72
)
to
Color
.
White
256
->
Color
(
0
xFFEDCC61
)
to
Color
.
White
512
->
Color
(
0
xFFEDC850
)
to
Color
.
White
1024
->
Color
(
0
xFFEDC53F
)
to
Color
.
White
2048
->
Color
(
0
xFFEDC22E
)
to
Color
.
White
else
->
Color
(
0
xFF3C3A32
)
to
Color
.
White
}
}
@Composable
private
fun
DirectionButton
(
text
:
String
,
onClick
:
()
->
Unit
)
{
Button
(
onClick
=
onClick
,
modifier
=
Modifier
.
size
(
60
.
dp
),
colors
=
ButtonDefaults
.
buttonColors
(
backgroundColor
=
Color
(
0
xFF8F7A66
)),
shape
=
RoundedCornerShape
(
8
.
dp
)
)
{
Text
(
text
=
text
,
fontSize
=
20
.
sp
,
color
=
Color
.
White
,
fontWeight
=
FontWeight
.
Bold
)
}
}
\ No newline at end of file
composeApp/src/commonMain/kotlin/com.dong.maxhap/demos/game/SnakeGameLogic.kt
0 → 100644
View file @
b6616e1f
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
composeApp/src/commonMain/kotlin/com.dong.maxhap/demos/game/SnakeGameScreen.kt
0 → 100644
View file @
b6616e1f
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
(
0
xFF90EE90
)
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
(
0
xFF8F7A66
)
),
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
(
0
xFF4A4A4A
)),
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
composeApp/src/commonMain/kotlin/com.dong.maxhap/demos/game/XiangqiLogic.kt
0 → 100644
View file @
b6616e1f
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
composeApp/src/commonMain/kotlin/com.dong.maxhap/demos/game/XiangqiScreen.kt
0 → 100644
View file @
b6616e1f
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
(
0
xFFDEB887
)),
horizontalAlignment
=
Alignment
.
CenterHorizontally
)
{
Row
(
modifier
=
Modifier
.
fillMaxWidth
(),
horizontalArrangement
=
Arrangement
.
SpaceBetween
,
verticalAlignment
=
Alignment
.
CenterVertically
)
{
Text
(
text
=
"🐘🐎 象棋"
,
fontSize
=
24
.
sp
,
fontWeight
=
FontWeight
.
Bold
,
color
=
Color
(
0
xFF8B4513
)
)
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
(
0
xFFDEB887
))
.
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
(
0
xFF8B4513
)),
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
composeApp/src/commonMain/kotlin/com.dong.maxhap/demos/test/HapPage.kt
View file @
b6616e1f
...
...
@@ -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
(
0
xFF4CAF50
),
onClick
=
{
showGame2048
=
true
}
)
Text
(
text
=
"✅ 应用体积已达到250MB目标!
"
,
fontSize
=
16
.
sp
,
fontWeight
=
FontWeight
.
Medium
GameButton
(
text
=
"🐍 贪吃蛇
"
,
color
=
Color
(
0
xFF4CAF50
)
,
onClick
=
{
showSnakeGame
=
true
}
)
Spacer
(
modifier
=
Modifier
.
height
(
20
.
dp
))
GameButton
(
text
=
"🔵🔴 五子棋"
,
color
=
Color
(
0
xFFFF9800
),
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
(
0
xFFF44336
),
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
composeApp/src/commonMain/kotlin/com.dong.maxhap/navigation/Navigation.kt
View file @
b6616e1f
...
...
@@ -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
),
...
...
composeApp/src/commonTest/kotlin/com.dong.maxhap/demos/game/Game2048LogicTest.kt
0 → 100644
View file @
b6616e1f
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
harmonyApp/AppScope/app.json5
View file @
b6616e1f
{
"app": {
"bundleName": "com.example.harmony
A
pp",
"bundleName": "com.example.harmony
a
pp",
"vendor": "example",
"versionCode": 1000000,
"versionName": "1.0.0",
...
...
harmonyApp/build-profile.json5
View file @
b6616e1f
...
...
@@ -7,11 +7,11 @@
"material": {
"certpath": "/Users/dongsq/.ohos/config/default_harmonyApp_1smLWfyPbDJ7_pD4O8WoXlm4q2fXfOBblaHaifpxJLc=.cer",
"keyAlias": "debugKey",
"keyPassword": "0000001A
1C16A6F1A54A452E6790A65F3DB1C7FD3322F45964DF1CF98F556D133408497516E6365B75C4
",
"keyPassword": "0000001A
26C8C7654C6CA614926DC75BAD03AB8162CE9CB7B3F2E82F93EB6695DB19FADA5B2CA39EB1F9
",
"profile": "/Users/dongsq/.ohos/config/default_harmonyApp_1smLWfyPbDJ7_pD4O8WoXlm4q2fXfOBblaHaifpxJLc=.p7b",
"signAlg": "SHA256withECDSA",
"storeFile": "/Users/dongsq/.ohos/config/default_harmonyApp_1smLWfyPbDJ7_pD4O8WoXlm4q2fXfOBblaHaifpxJLc=.p12",
"storePassword": "0000001A
F01134AC8226D0D9D7D38E0651C89DA4E84B40A74EEC485CF602D1AF1FAECD976A1110EF47BF
"
"storePassword": "0000001A
141E91C553F22791EB8EC3DB56317590E0103FB77BDDDD4475F35AE337A69344FE627E703C8B
"
}
}
],
...
...
harmonyApp/entry/libs/arm64-v8a/libkn.so
View file @
b6616e1f
No preview for this file type
harmonyApp/entry/src/main/cpp/include/libkn_api.h
View file @
b6616e1f
This diff is collapsed.
Click to expand it.
harmonyApp/entry/src/main/resources/base/element/string.json
View file @
b6616e1f
...
...
@@ -10,7 +10,7 @@
},
{
"name"
:
"EntryAbility_label"
,
"value"
:
"label"
"value"
:
"label
1
"
}
]
}
\ No newline at end of file
harmonyApp/entry/src/main/resources/base/media/additional_asset_1.dat
0 → 100644
View file @
b6616e1f
File added
harmonyApp/entry/src/main/resources/base/media/additional_asset_2.dat
0 → 100644
View file @
b6616e1f
File added
harmonyApp/entry/src/main/resources/base/media/large_asset_1.dat
0 → 100644
View file @
b6616e1f
File added
harmonyApp/entry/src/main/resources/base/media/
medium_file_2.bin
→
harmonyApp/entry/src/main/resources/base/media/
large_asset_4.dat
View file @
b6616e1f
No preview for this file type
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment