乾.坤
本帖最后由 乾.坤 于 2016-4-28 22:14 编辑

大家好,我是乾坤。今天带来的是一个偏向算法性的作品——分数智能排序算法。也就是解决一堆分数从高到低排序的问题(这里要说一下,计分板上的排序是给玩家能直观看到的,而能被系统读取的顺序则需要输出到另一个计分板,看图


比如现在有9个玩家玩游戏,分数分别如上所示,虽然系统已经帮我们排好了先后顺序,但这个顺序计分板并不能读取,那就需要排序到另一个计分板:


就像这样,分数从高到低从第一名排到第九名(可见,也是可以处理重分的),这样的话我需要对比赛分数并列第4名的玩家执行命令只需要
/execute @e[score_Order_min=4,score_Order=4] ...
即可)
其实排序算法也是很常用的,小游戏制作选出最高分的问题也一直会被问到,而把排序拓展到n位之后,应用的范围就更广泛了。
下页使用方法


2021.12 数据,可能有更多内容大家好,我是乾坤。今天带来的是一个偏向算法性的作品——分数智能排序算法。也就是解决一堆分数从高到低排序的问题(这里要说一下,计分板上的排序是给玩家能直观看到的,而能被系统读取的顺序则需要输出到另一个计分板,看图



比如现在有9个玩家玩游戏,分数分别如上所示,虽然系统已经帮我们排好了先后顺序,但这个顺序计分板并不能读取,那就需要排序到另一个计分板:



就像这样,分数从高到低从第一名排到第九名(可见,也是可以处理重分的),这样的话我需要对比赛分数并列第4名的玩家执行命令只需要
/execute @e[score_Order_min=4,score_Order=4] ...
即可)
其实排序算法也是很常用的,小游戏制作选出最高分的问题也一直会被问到,而把排序拓展到n位之后,应用的范围就更广泛了。
下页使用方法






进游戏会看到两排按钮,下面6个,上面3个(最左和最右的按钮在侧面),先用按钮选好分(分数自己随意)之后是这样:



由于没有排过名,所以只有选中的分数,排序(左边第一个按钮)之后就会多出一排分数:



这个分数就是你刚刚设定的那个分数的历史排名,因为第一次排序所以当然是1(历史最高)



这时如果你再选个分,就会有两个分数了。



这是上面的三个命令方块。中间是重置整个系统,左边是查看你曾经选定过的分数,右边是所有分数的总排名


下页原理讲解



总共30多个命令方块,一起来看看。
算法计分板:
1.Count (其中的假名Count用于处理同分)
2.Score (储存需要排序的分数)
3.Order (将重新排序后的分数计入这个计分板)


核心命令:

代码:

  1. /scoreboard players operation @e[name=Holder,type=ArmorStand] Score > @e[tag=Compare2,c=1] Score
有必要强调一下这个命令的用法:
scoreboard players operation <targetName> <targetObjective> <operation> <selector> <objective>
">" 取较大值: 如果selector的分数比 targetName的分数大,则把targetName的分数设为 selector的分数。
------Minecraft Wiki

简而言之 A>B的意思就是:若B>A,则令B=A 我们可以利用这个做出排出一系列分数的第一名,方法就是刷出一个Marker来和所有分数使用一遍“>”运算,这样,全部比对完以后Marker的分数就是探测范围内的最大分数了,然后将Marker的值与所有分数相减,分数为0的那个(些)就是第一名了。你可以这样理解:一次“>”运算是一个点的运动(一组分数的比对),那么将所有点纵向运动起来(将每组分数比对过去)就构成了一条“线”(一次第一名的探测),也就是所谓的“点动成线”


那么,如何“线动成面”呢?
我们可以这样做:将第一次选出的第一名剔除在外,然后在剩余的分数中利用之前的方法选出现在的第一名。那第二次选出的第一名就是第二名了,依次类推,直到每个分数都当过一次第一名之后就意味着每个分数都有自己的“位置”(或者说顺序)了。
当然,“点动成线,线动成面”意味着我们需要有一个两层的控制系统,告诉电脑接下来该怎么排序。
主要的思路就是上面这些,我们看看在游戏中的对应关系。
最开始只执行一次的部分:

代码:

  1. icb:/blockdata ~ ~ ~ {auto:0b}
  2. /scoreboard players tag @e[type=ArmorStand,name=Test] add Compare1
  3. /blockdata ~-1 ~ ~ {auto:1b}
然后就是一层控制(探测是否所有的分数都进行过排序,tag=Compare1就是还没有排序后分数的分数)

代码:

  1. /blockdata ~ ~ ~ {auto:0b}
  2. /blockdata ~-4 ~ ~ {auto:1b}
  3. /testfor @e[type=ArmorStand,tag=Compare1]
  4. cond:/blockdata ~-2 ~ ~ {auto:0b}
  5. cond:/blockdata ~-2 ~ ~ {auto:1b}
  6. /blockdata ~-24 ~ ~ {auto:1b}
如果没有全部都经过排序,就执行下一层,即“第一名”的选择
这些是在二层中只执行一次的:

代码:

  1. /blockdata ~ ~ ~ {auto:0b}
  2. /summon ArmorStand ~ ~ ~ {Marker:1b,CustomName:Holder,NoGravity:1b,Invisible:1b}
  3. /scoreboard players tag @e[type=ArmorStand,name=Test,tag=Compare1] add Compare2
  4. /blockdata ~-1 ~ ~ {auto:1b}
下面是二层控制(探测此轮“第一名”是否已经探测完成,即Marker是否对每个待选分数进行过“>”运算,tag=Compare2指的是未运算过的分数):

代码:

  1. /blockdata ~-4 ~ ~ {auto:1b}
  2. /testfor @e[type=ArmorStand,tag=Compare2]
  3. cond:/blockdata ~-2 ~ ~ {auto:0b}
  4. cond:/blockdata ~-11 ~ ~ {auto:1b}
如果2级运算也没有完成,系统会执行以下命令:

代码:

  1. /blockdata ~ ~ ~ {auto:0b}
  2. /scoreboard players operation @e[name=Holder,type=ArmorStand] Score > @e[tag=Compare2,c=1] Score
  3. /scoreboard players tag @e[tag=Compare2,c=1] remove Compare2
  4. /blockdata ~18 ~ ~ {auto:1b}
如果完成了,就执行这些(顺序对号入座,当然,理所当然的,加了重分的处理):

代码:

  1. /blockdata ~ ~ ~ {auto:0b}
  2. /scoreboard players operation @e[type=ArmorStand,name=Test] Score -= @e[type=ArmorStand,name=Holder] Score
  3. /scoreboard players add Count Count 1
  4. /scoreboard players operation @e[type=ArmorStand,name=Test,score_Score_min=0,score_Score=0] Order = Count Count
  5. /scoreboard players tag @e[type=ArmorStand,name=Test,score_Score_min=0,score_Score=0] remove Compare1
  6. /execute @e[type=ArmorStand,name=Test,score_Score_min=0,score_Score=0] ~ ~ ~ /scoreboard players add Count Count 1
  7. /scoreboard players remove Count Count 1
  8. /scoreboard players operation @e[type=ArmorStand,name=Test] Score += @e[type=ArmorStand,name=Holder] Score
  9. /kill @e[type=ArmorStand,name=Holder]
  10. /blockdata ~24 ~ ~ {auto:1b}
现在,让我们回到一级控制处,如果第一层探测不到未排序的分数,那么就算大工告成,每个分数都有自己对应的顺序了,收尾即可:

代码:

  1. /blockdata ~ ~ ~ {auto:0b}
  2. /scoreboard players reset Count Count
  3. say ok

至此,分数排序全部完成。


下页闲聊+存档


看到BBS是没有特别系统的排序算法(可能是用得少吧),就特地写了一个稍微集成点的,希望能帮助到想要提高水平的命令新人2333,排序其实在做系统的时候也是会用到的,当然,方法仅供参考,仅是抛砖引玉罢了,大触们见笑了。
特别感谢 @玄素 给我提供了脑洞


存档下载(本人制作采用的是1.9.3pre2)
[TML-乾坤]分数智能排序算法





来自群组:The Minecraft Lover



喵双籽
看不明白O-O

lzs1234
看到核心原理,我又看了一遍,还看了一遍,明白了
好东西,之前pca也说过当时没仔细想,还是觉得要探测分数,很麻烦XD
这个方法才算看懂。总而言之就是好像一堆球,我在其中选取,取出最大的一个,然后把这个拿出来,重复这个过程直到都排完。而且全自动。
不错

hhttll
命令方块真的不适合用来搞这种东西。mc 应该像 space engineers 一样,可以直接嵌入代码。这样大家都方便,效率也高

gunman
hhttll 发表于 2016-4-29 05:30
命令方块真的不适合用来搞这种东西。mc 应该像 space engineers 一样,可以直接嵌入代码。这样大家都方便, ...

MC本来就是java语言的,要是可以直接调用class文件,比如排序类就省事了

hhttll
gunman 发表于 2016-4-29 09:21
MC本来就是java语言的,要是可以直接调用class文件,比如排序类就省事了

你说的这个是 mod

我觉得 mod 有几个很重要的缺陷:
1. 他是跟客户端走的而不是跟地图走的。你不会为了一个一般般的地图去装一个 mod,太麻烦了。
2. 不安全。mod 其实可以写病毒的,目前没有一个杀毒软件能杀 jar 的病毒(来源:andylizi)
3. 太过繁琐。写一个 mod 真是麻烦的要死。新人完全不知从何做起。java 刚开始特别困难。

出于以上几点,我觉得最好的解决方案是:
使用内嵌 js 或 lua 的方法(比如直接把 js 脚本或者 lua 脚本写到命令方块里)。脚本跟地图走且脚本的每一行代码都在 mc 的虚拟环境里执行,保证不会有病毒。mod 依然可以存在,mod 主要用于添加物品或者大幅度修改游戏机制,所以可以说 mod 和 内嵌脚本完全没有重合之处。你看着好了,用不了多久,肯定会有人做出一个让 mc 支持内嵌 js 的 mod 的。

机器人WBW
本帖最后由 林扬骐 于 2016-4-29 17:31 编辑

思路应该还是易懂的,但是后面一堆看吐了啊啊啊,用作排名的话肯定是要用json的吧233

另外解压文件坏了快修

pca006132
hhttll 发表于 2016-4-29 05:30
命令方块真的不适合用来搞这种东西。mc 应该像 space engineers 一样,可以直接嵌入代码。这样大家都方便, ...

为何命令方块不适合呢?麻烦吗?然而CB就是一种游戏模式,开心就好了

kj415j45
你的想法与选择排序已经很接近了:遍历数组选出最小/最大放在数组头,然后继续选择最小/最大放在下一位
但是这个算法的时间复杂度为O(n^2),属于平方级算法,当n值大时排序速度慢

你可以在搜索第一名的同时搜索最后一名,这样可以稍微加快排序速度

而且既然你额外使用了计分版,那你就可以在这个计分版里只存1个分数数据,用它来交换分数就行了,像你这样排序结果单独存又转存到原计分版是明显浪费时间的,而且实际上也浪费了空间


然而由于CB还不能用递归,所以也没有更好的算法可用了(比如快排,时间复杂度为O(n*log以2为底n)

840196733mw
计分板不到家啊 现在才知道有大于小于这些用法

又有新的技能解锁了

新技能 排序 Get √

hhttll
pca006132 发表于 2016-4-29 17:32
为何命令方块不适合呢?麻烦吗?然而CB就是一种游戏模式,开心就好了

不适合啊.. 弄个排序这么复杂。

乾.坤
kj415j45 发表于 2016-4-29 19:51
你的想法与选择排序已经很接近了:遍历数组选出最小/最大放在数组头,然后继续选择最小/最大放在下一位{:69 ...

嗯,后续我会再改进的,谢谢建议XD

乾.坤
kj415j45 发表于 2016-4-29 19:51
你的想法与选择排序已经很接近了:遍历数组选出最小/最大放在数组头,然后继续选择最小/最大放在下一位{:69 ...

emm其实显示计分板只是为了演示用,其实本来的系统是没有转存的hh,不过我倒是能优化一下cb,有了一个比较好的方法XD

走失的小怕
似懂非懂

xiaopeng666
这些编码躲什么鬼看不懂{:10_492:}