多人位置重排系统
介绍
这个由@ZheaEvyline创建的多人位置重排系统(更准确地说应称为多人位置错位系统),能够将所有选定目标彼此随机重新定位,并确保没有任何目标停留在原始位置。
错位排列是指对'N'个元素进行排列时,没有任何元素出现在其原始位置。
例如,若玩家1位于(0, 0, 1),玩家2位于(0, 0, 2),玩家3位于(0, 0, 3),那么玩家1只能被重定位到(0, 0, 2)或(0, 0, 3)。此规则同样适用于其他所有玩家。
本功能包的核心特性:
- 确保在单个游戏刻内完成错位排列
- 使用最少的迭代次数(重复/循环)
- 支持跨维度操作
该功能包设计支持无限数量的目标。但由于基岩版的限制,当达到10,000次函数执行上限时,系统将停止运行。
举例来说,对100个目标进行错位排列仅需4-6次迭代,初始化阶段执行7条指令,每次迭代执行9条指令。总计约60条指令,远低于函数执行上限。
迭代次数会随元素数量增加而相应增长。
以下是1至10个元素时可能的错位排列数量参考表:
| 元素数量(N) | 可能的错位排列数(D(N)) |
|---|---|
| 1 | 0 |
| 2 | 1 |
| 3 | 2 |
| 4 | 9 |
| 5 | 44 |
| 6 | 265 |
| 7 | 1,854 |
| 8 | 14,833 |
| 9 | 133,496 |
| 10 | 1,334,961 |
随着元素数量增加,错位排列的可能性数量会急剧上升。
方法实施步骤
- 将每个目标随机传送到另一个目标的位置(非其原始位置)
- 若多个目标被传送到同一位置,则将该位置分配给其中一个目标,并对剩余目标重复此过程
- 若最后剩余的目标除原始位置外无其他可用位置,则将当前占据该位置的目标移回其原始位置,从而腾出位置给最终目标
示例可视化:

功能实现
需要建立ID系统来为所有目标从1到N编号,以便追踪每个目标的原始位置。我们将在tick.json中运行此文件来自动分配ID。
## 为新玩家注册ID目标
scoreboard players add @a wiki:id 0
## 创建新ID
execute if entity @a[scores={wiki:id=0}] run scoreboard players add .Total wiki:id 1
## 分配新ID
scoreboard players operation @r[scores={wiki:id=0}] wiki:id = .Total wiki:id每次需要错位排列所有目标位置时,运行以下函数(只需运行一次):
/function wiki/derange_position/initiate
## 生成位置标记
execute at @a run summon armor_stand "wiki:position_marker" ~~~
## 将原始位置保存至忽略列表
execute as @a at @s run scoreboard players operation @e[type=armor_stand,name="wiki:position_marker",r=0.01,c=1] wiki:id = @s wiki:id
## 为所有目标启动位置错位过程
function wiki/derange_position/process
## 若最终玩家仍有有效位置可用,则再运行一次处理过程
execute if score .Players.NotAllocated wiki:count matches 1 unless score @a[tag=!wiki:pos.allocated,c=1] wiki:id = @e[type=armor_stand,name="wiki:position_marker",c=1] wiki:id run function wiki/derange_position/process
## 若最终玩家除原始位置外无其他可用位置,则解决冲突
### 将已分配位置的玩家移回冲突玩家的原始位置,从而腾出位置给冲突玩家
execute as @a[tag=!wiki:pos.allocated] at @s run tp @r[tag=wiki:pos.allocated,r=0.01] @e[type=armor_stand,name="wiki:position_marker",c=1]
### 移除冲突玩家的位置标记和标签
kill @e[type=armor_stand,name="wiki:position_marker"]
tag @a[tag=wiki:pos.allocated] remove wiki:pos.allocated当单个目标除原始位置外无其他可用位置时,最后3条指令将解决冲突。我们称之为冲突是因为当这种情况发生时,该目标将位于另一个目标的已分配位置。
实际的随机错位过程将由以下函数执行:
## 移动到不同位置
execute as @a[tag=!wiki:pos.allocated] at @s run function wiki/derange_position/teleport
## 若返回原始位置:再次移动
execute as @a[tag=!wiki:pos.allocated] at @s if score @s wiki:id = @e[type=armor_stand,name="wiki:position_marker",r=0.01,c=1] wiki:id run function wiki/derange_position/teleport
## 添加标签以忽略已分配位置的玩家
execute as @e[type=armor_stand,name="wiki:position_marker"] at @s run tag @a[tag=!wiki:pos.allocated,r=0.01,c=1] add wiki:pos.allocated
## 移除已分配位置标记
execute as @a[tag=wiki:pos.allocated] at @s run kill @e[type=armor_stand,name="wiki:position_marker",r=0.01,c=1]
# 实体计数器
## 获取未分配位置玩家数量
scoreboard players set .Players.NotAllocated wiki:count 0
execute as @a[tag=!wiki:pos.allocated] run scoreboard players add .Players.NotAllocated wiki:count 1
## 若有2+玩家未分配位置:循环执行函数
execute if score .Players.NotAllocated wiki:count matches 2.. run function wiki/derange_position/process- ❌️
tp @s @r[type=armor_stand,name="wiki:position_marker",rm=0.01]
直接使用此指令传送到新位置仅适用于当前维度。因此,我们改用以下三条指令的函数来实现跨维度兼容:
tag @e[type=armor_stand,name="wiki:position_marker",r=0.01] add wiki:pos.ignored
tp @s @r[type=armor_stand,name="wiki:position_marker",tag=!wiki:pos.ignored]
tag @e remove wiki:pos.ignored为使我们的函数实际工作,需要在世界中添加以下目标:
scoreboard objectives add wiki:wiki:id dummy
scoreboard objectives add wiki:wiki:count dummy若希望在加载世界时自动添加目标,可创建以下函数文件:
## 初始化
### 添加目标
scoreboard objectives add wiki:world dummy
### 注册到目标
scoreboard players add .Initialised wiki:world 0
## 执行的指令
execute if score .Initialised wiki:world matches 0 run function wiki/scoreboard/objectives/add_all
## 标记为已初始化
scoreboard players set .Initialised wiki:world 1Tick JSON文件
最后创建tick.json文件:
{
"values": [
"wiki/event/worlds/on_initialise",
"wiki/scoreboard/players/id"
]
}文件夹结构
- 📝id.mcfunction
- 📝add_all.mcfunction
- 📝on_initialise.mcfunction
- 📝initiate.mcfunction
- 📝process.mcfunction
- 📝teleport.mcfunction
- 📝tick.json
- 📝manifest.json
- 🖼️pack_icon.png
下载功能包
为方便起见,可在此处下载功能包的.mcpack文件:
只需在多人游戏中激活此包并在需要时运行以下指令:
/function wiki/derange_position/initiate


