【梦话连篇】机器人的基石——浅谈触发器(上、下)
本帖最后由 hijacker 于 2013-2-18 08:29 PM 编辑开篇的一点说明:
玩MUD、学做机器人的过程中产生了一些自认为有趣的想法,想和泥友们分享。因为很多东西只停留在“想法”的阶段,所以把它们冠以总称“梦话连篇”——本想把它发到闲聊版,再三考虑后还是决定驻扎技术版,希望不会被各路技术帝鄙视。是否真的把这些内容写成一个系列要视情况而定,先发一篇基础一些的,关于触发器。
北大侠客行MUD,中国最好的MUD 本帖最后由 hijacker 于 2013-1-8 11:04 AM 编辑
机器人的基石——浅谈触发器(上)
机器人的一切动作都是对服务器回显信息的反馈。这个和一串串字符打交道的过程真的非常奇妙:你要从几十、上百条看似杂乱无章的各类信息中找到自己那盘菜,然后从中捕捉具有关键含义的只言片语,而且整个过程中还可能需要对捕获信息进行一些诸如大小写转换之类的处理。最后,你将对着这些“只言片语”冥思苦想,尝试各种方法来让计算机明白这是什么和应该如何去回应——如果你认同我的以上感觉,并和我一样深深迷恋着这个文字堆砌的神奇世界,那么我们继续。
总体来看,触发器的工作过程就是两个环节:匹配、动作。让我们把注意力集中在最能表现触发器特色的“匹配”环节,意即找到它、抓住它的过程。
很多人,不,可以说会玩MUD的人全都会做触发器,但不知道有多少人会去思考它的工作原理呢?我猜(非专业人士有根据YY,不合理请指出),它的工作原理大约是这样的:每当服务器向客户端传送一行字符串,客户端就会把所有的(请注意,所有的)触发器中的文本项(即zMUD 4.62中的“句型”)依次和这行字符进行比照——成功了,就执行后面的语句;不成功则没有反应。很简单,是不是?但我认为,意识到这个细节,对玩家的机器人水平来说将会是一个质的飞跃。举个例子:若你的机器人里有一个触发,那么对每一行回显信息,客户端都要比对1次;若你有100个触发,客户端就会比对100次。后者处理这一行的时间可能是前者的100倍。好吧,也许这可能只是1*1 us和1*100 us的区别,没人会去计较这么一丁点儿时间,可当被乘数或乘数非常大时,倍数产生的差距就不可忽视了,我见过有人写的zMUD机器人运行起来非常迟钝,非常影响心情……而具体到每一个触发器,也有质量好坏的差别。再次举个例子:触发器A是#tri {“^你好”} {smile},触发器B则是一种偷懒的写法#tri {“你好”} {smile},当服务端发给你一行字符串C“键盘手杰克(hijacker)告诉你:hi”时,触发器B的工作流程大约是: B中的“你”字和C中的“键”不匹配->B中的“你”和C中的“盘”不匹配->……->B中的“你”和C中的“诉”不匹配(机器人:主人我好累啊!)->B中的“你”和C中的“你”匹配->B中的“好”和C中的“:”不匹配(机器人:主人我好不容易匹配成功了一个“你”字,这下又得从头再来了555)->B中的“你”和C中的“h”不匹配->B中的“你”和C中的“i”不匹配->判断结束,结果是C和B不匹配(实际情况可能更加复杂,但大致如此);触发器A的工作方式则是:A中的“你”和C中的“键”比较,发现不匹配,直接结束判断,结果为不匹配——因为有了“^”符号,所以A比B省略了很多不必要的判断。如果机器的运气不好,杰克tell你很长一段话,那么B的判断工作会更加地漫长,而A就很让机器省心——看吧,即使只有一条触发,匹配时间也可能相差很多呢!
上面一段只说了一个问题,就是触发器在匹配环节的效率问题,从中可以总结出降低机器负担的两种方法:1. 尽量减少触发器的数量; 2. 尽量把每个触发器写得“虎头蛇尾”——行首严格判断,行末宽松判断。对于第二点特别说明一下:“虎头”前面已经描述过其典型代表 “^”的合理运用;“蛇尾”是怎么回事呢?我想把这个问题留给感兴趣的人思考,提示:留意#tri {你好*}{}和#tri {你好啊你是哪个门派的?}{}的区别。
好了,以上是我对触发器的有感而发,算不得专业见解,只是说出了自己的一些想法,希望通过交流来促进学习。如果你耐心地看到了文章的这个位置,我会非常开心。对本文的不认同之处和新的想法,请一定回帖。抱拳。 本帖最后由 hijacker 于 2013-1-8 11:06 AM 编辑
机器人的基石——浅谈触发器(下)
了解了触发器的基本工作原理,我们可以制作自己的触发器了,来享受DIY的乐趣吧!怎样DIY触发器?先来看一个简单的例子。以下是一个特别的吐纳机器人
#tri {((*))} {#if %pos("你吐纳完毕",%1) {exert regenerate; tuna 100};#if %pos("你现在精不足",%1) {sleep};#if %pos("你一觉醒来",%1) {tuna 100};#if %pos("你刚在三分钟内睡过一觉",%1) {#wa 3000;exert regenerate;tuna 100}} {} 519
为了通俗,我尽量选择用ZScript来描述问题,MUSHClient和Tintin++的原理是相同的。注意到了吗?在zMUD看来,以上只是一个触发器,但我们却用它完成了四个触发器的任务:用(*)把每一行的所有内容捕获到%1后,剩下的匹配规则完全是由我们自己制定的(这里主要用到了pos函数)。有人会说,这样折腾多麻烦啊,一点都不如正常方法容易!的确,乍一看上去,这完全有点自找麻烦的味道在里面。但我要说的事,这种方法给了我们一样东西,那就是“自由”(对对对,就是那个举着火炬的女神)!在zMUD中这种自由可能不够凸显,我们把目光转向可以使用脚本语言的客户端(下文对zMUDer可能没什么意义,抱歉),比如MUSHClient。首先,我们把DIY触发器的所有文本及其对应动作、选项等放入一个表(table)中——表中的每一项相当于一个被模拟出来的触发器内容,然后像zMUD那样用(*)捕获一行,再把这行和表中的每行进行比对,比对成功则执行对应动作。对应于zMUD里的#tri,我们也可以在MC里写一个trigger函数,示例如下:
function trigger(name, match, script, temp, args, keep, priority, color)
table.insert(triggers, {
["name"] = name, --触发器名字
["match"] = match, --触发器文本
["script"] = script, --匹配成功后的动作,以函数方式传递
["temp"] = temp, --是否是临时触发
["args"] = args, --前面的script函数的各种参数
["priority"] = priority, --优先级,数字类型
["color"] = color, --是否是颜色触发,预留项,还没有想好怎样实现
["keep"] = keep, --相当于MC原装触发器中的“保持有效性”
})
end
当我们用捕获整行信息时,就可以把这行信息和triggers表里的每项进行比对了,比对的框架大约是(会有人对这个感兴趣吗?)
for i, v in pairs(triggers) do --triggers表事先要根据优先级排好顺序
if line, v.match 匹配成功 then
执行v.script(args)
if v.temp表明本触发是临时触发 then
删除该触发
end
if 不保持有效性 then
break --不继续对比
end
end
end
这样一来,我们就几乎可以用纯lua模拟一系列MC触发器,我想TinTin++等其他支持脚本语言的客户端应该也能实现类似的功能(未验证)。好咧!关于DIY触发器的表演就先到这里,不得不说,代码真的是很能占字数的:-)
不知道各位看官此时有没有紧锁眉头满脸茫然,我且硬着头皮继续说下去。这样做到底有何意义?如前所述,它给了我们自己制定规则的自由。这么麻烦的自由到底有何价值?试想,它帮助我们在编写机器人时基本脱离了MC、TT++等客户端制定的大部分规则,那我们是不是可以用同一套脚本,既可以运行在MC上,又可以只进行少量的修改后直接运行在TT++上呢?在我的设想中,这应该是可以实现的(未验证,不然怎么叫“梦话连篇”呢,:-),至于不支持脚本语言的zMUD 4.62,我大胆猜测它可以通过DDE实现与脚本语言的交互,虽然我还不了解DDE是什么:-) 我梦想着未来会不会有这么一天——发在技术版的机器人再也不用区分zMUD/MUSHClient/TinTin++/其他...那将是一件多么cool的事! 技术文这么长,精了,不要烂尾哦~ 一、机器人的基石——浅谈触发器(上)
机器人的一切动作都是对服务器回显信息的反馈。这个和一串 ...
hijacker 发表于 2013-1-8 01:07 http://pkuxkx.net/forum/images/common/back.gif
看起来很像程序员= = 开篇的一点说明:
玩MUD、学做机器人的过程中产生了一些自认为有趣的想法,想和泥友们分享。因为很多 ...
hijacker 发表于 2013-1-7 05:05 PM http://pkuxkx.net/forum/images/common/back.gif
来点mush 必须补上mush的,不然cut掉 回复 4# ruoyu
多谢鼓励,这个精华对我来说意义非常重大。不过你是看字数的吗?这个这个…… 回复 6# qdz
回复 7# cysp
mush来了(准确说是lua来了),其实不过是个思路,和具体用哪种客户端关系不是太大 路过支持一下
页:
[1]
2