<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    常言笑的家

    Spring, Hibernate, Struts, Ajax, RoR

    ZMUD機器人制作(下篇)

    第十章 機器人實例--朝廷守門機器人

    這是一篇介紹機器人制作方法的帖子,因此盡管你可能不需要守門機器人,還是可以看下守門機器人的制作過程,至少沒有壞處不是?
    本章注釋非常詳細,盡管你可能不了解守門任務,仍然能很容易讀懂。

    建議:做機器人之前先總體規劃一下,打算做成一個什么樣的機器人.如果你不這樣做,很多時候要花費更多的時間來修改,事實上修改比制作更費勁.更糟糕的是,有時候沒法修改,你不得不重新做.

    機器人的用途:僅僅為了培養朝廷大米,不適用于主id

    希望機器人具備的特點:安全,穩定,效率最大化,適用面最大化,界面豐富

    安全方面:擁有應付蒙面殺手和盜寶人的安全措施

    穩定方面:防發呆,斷線自動重連

    效率最大化方面:
    這是專門為大米寫的機器人,守完門之后沒有精力幫大米做任務,所以只希望守完門之后經驗值盡可能的高,對守門的速度和花費的時間沒有要求.每1次守門都力求獲得8000左右的經驗,由此確定效率a和b
    (a):守800次門需要很長時間,不急,慢慢掛
    (b):如果出現2個及以上的玩家同時守一個門,會影響守門獎勵,此時退出游戲放棄守門
    守完門之后有7m多,這個時候再學技能,前200級可以直接學了,高經驗高讀書寫字高內功支持下full技能也更快更省潛能,所以晚上不守門的時候沒事可做了.由此確定效率c
    (c):天亮時上線守門,守完門下線

    適用面方面:沒什么好說的,只適用于朝廷.機器人里保存好帳號密碼就可以了.

    界面豐富方面:設計好status bar,顯示出我關心的所有數據.
    最終確定為:每次守門殺死壯漢數目@zhuanghan,獲得經驗@expgrain,當前貢獻度@gongxian,當前經驗,守門是否成功@success

    開始制作

    步驟一:先做好主體部分:接任務做任務完成任務下線

    #tri {你向多隆打聽有關『守門』的消息。} {#t+ job}
    //多隆的回答全部取一個class名:job
    #tri {這里沒有這個人。} {#t- job;#wa 20000;ask duo about job} {job}
    #tri {但是很顯然的,多隆現在的狀況沒有辦法給你任何答覆。} {#t- job;#wa 20000;ask duo about job} {job}
    //多隆死了或者暈了,等待20秒再要任務
    #tri {多隆說道:「大人位高權重,已經不用干這種活了。」} {#t- job;#play xxx.wav;#var zhanghao {};#var mima {};quit} {job}
    //守門結束了,清空帳號密碼阻止自動連線,播放一首音樂輕松下
    #tri {多隆說道:「大人需要升遷之后再繼續作守門任務。」}
                    {#t- job;do 3 s;do 4 w;s;s;ask ming zhu about bonus;ask ming zhu about 升遷;n;n;do 4 e;do 3 n;ask duo about 守門} {job}
    #tri {多隆說道:「守門只有早晚兩班,你到時間再來吧。」} {#t- job;#timer on} {job}
    //上線時間不一定準確,此時打開tick timer以確保不會發呆10分鐘掉線,然后等待天色觸發
    設置tick timer,每300秒輸入1個hp命令,僅僅為了保證不會發呆掉線
    #tri {你的任務是去(*)守門。} {#t- job;%1} {job}
    //注意,這里的做法很巧妙,用任務地點做為命令,請看下面定義的alias,你就會明白了
    #alias {東直門} {do 3 s;e;guarding}
    #alias {阜成門} {do 3 s;do 7 w;do 3 s;guarding}
    //alias是可以取中文名字的,直接用任務地點制作路徑alias,可以省去很多#case#if的判斷
    //鑒于篇幅考慮,不一一寫出18個地點的alias了,此文主要介紹機器人的制作方法,請原諒

    注:我看到有的人把多隆吩咐任務這句話做成18個trigger,每個trigger單獨指定去目標地點的路徑和返回的路徑,或者為每個門編上號碼,然后通過號碼來確定去和回的路徑,需要用一個很長的#case命令,比我的這個方法復雜了很多很多倍

    注:前面章節中曾經提到過能簡潔堅決不要復雜,越簡潔的東西越穩定;而且一個復雜的大型機器人擁有很多觸發變量和別名,簡潔一倍,花在修改和測試上的時間可能就節約10倍,至少在本例中假如我guarding拼寫錯誤,我只需要改一個trigger而不是去改18個trigger

    注:由于這里采用守門結束下線的方法,所以不需要返回的路徑。鑒于有的同學需要返回多隆那里打坐吐納或者繼續守晚上,下面用紫色文字給出返回的實現方法,看是否比你的方法簡單
    #tri {你的任務是去(*)守門。} {#t- job;%1} {job}
    #alias {東直門} {do 3 s;e;guarding;#var fanghui 東直門b}
    #alias {東直門b} {w;do 3 n}
    #tri {城門的鐘敲了兩下,你的換班時間到了。} {halt;#wa 3000;halt;@fanghui}
    //在去的時候將返回的命令保存在@fanghui里面,回來時全部統一的輸入@fanghui就可以了,如果你細心的話,類似方法在第三章中就已經介紹過了

    步驟二:把所有天亮的天色描述做成trigger

    #tri {【 天色 】風吹起來了,和著東升太陽的萬道金光,清晨不似從前那般涼爽} {#timer off;ask duo about 守門} {天色}
    //別忘了關掉tick timer,為了篇幅考慮,下面只給出所有的天色描述,trigger的做法完全一樣
    //最好為所有的天色描述去一個class名:天色,然后單獨保存起來便于以后加載
    【 天色 】金黃色的太陽剛從東方的地平線上冉冉升起
    【 天色 】起風了,樹木光禿禿的在風中瑟瑟的發抖
    【 天色 】起風了,新發芽的枝葉在風中幽雅的搖弋著
    【 天色 】起風了,一片片發黃的樹葉,紛紛揚揚隨風飄起
    【 天色 】秋雨淅瀝瀝地下了起來,涼絲絲的
    【 天色 】如霧的小雨帶著春意,飄向每個閑情的窗口
    【 天色 】太陽從東方的地平線冉冉升起,放射著耀眼的光芒
    【 天色 】太陽從東方的地平線升起了
    【 天色 】太陽從東方的地平線升起了。一片紅紅的朝霞
    【 天色 】太陽從東方的地平線升起了,蒼白無力地發射著光芒
    【 天色 】太陽從東方升起來了
    【 天色 】太陽剛從東方的地平線升起
    【 天色 】太陽露在東方的地平線上,顯示了一天的勃勃生機
    【 天色 】太陽升起來了,躲在云層后面呼呼地喘著氣
    【 天色 】太陽吞吐著萬道金芒,躍出地平線,大地一片金黃
    【 天色 】太陽在東方的天邊吞吐著萬道金光,太陽升起來了
    【 天色 】天光終于放亮了,風裹著云在天上飛速的跑著
    【 天色 】微風帶著一縷泥土的芳香吹了起來,天光放亮了
    【 天色 】細若牛毛的春雨淅瀝瀝地下了起來,早起的小鳥歡快地呢喃著
    【 天色 】小雪夾雜著雨無聲無息的下著
    【 天色 】小雨正淅瀝瀝地下著
    【 天色 】雪加著綿綿的小雨無聲的下著,不一會地上就濕了
    【 天色 】一道耀眼的金光倏的從東方的天邊射了出來,一輪紅日噴薄而出
    【 天色 】一道耀眼的金光倏的從東方的天邊射了出來,一輪紅日升了起來
    【 天色 】一道耀眼的金光倏的從東方的天邊射了出來,一輪紅日躍出天際
    【 天色 】一輪紅艷艷太陽剛從東方的地平線升起
    【 天色 】又是一個新年的早晨,從東方的天邊射了萬道霞光,一輪紅日躍出天際

    步驟三:制作守門過程

    #tri {你一叉腰,對身旁的官兵道:*。} {#t+ 守門;#var success 進行中.....;#var zhuanghan 0;#var expgrain 0}
    //這句話出現表示守門正式開始,打開守門class,接下來所有的觸發都取一個class名:守門
    //在定義status bar的時候要用到@success,用來顯示守門前,守門成功,守門失敗,守門進行中.....,守門被干擾5種情況,方便查看
    //@zhuanghan和@expgrain也用在status bar中,用來查看守門過程中殺敵數目和守門獎勵
    #tri {江湖漢子快步走了過來。} {pancha jianghu hanzi} {守門}
    //盤查江湖漢子。白天守門任務有2類,其中之一需要盤查江湖漢子。

    #tri {*對著(*)大喝道:擋我者死!} {#if (%1!=你) {#t- 守門;退出}} {守門}
    #tri {江湖漢子一言不發,陡然向(*)發難。} {#if (%1!=你) {#t- 守門;退出}} {守門}
    //其他玩家也在同一個地方守門,受到干擾,退出游戲

    #tri {官兵攔住(*)說道:看告示了沒有?您想進帶著*進城吶?先問問*大人吧!} {#if (%1!="壯漢") {#var name %1;look}} {守門}
    //官兵可能攔住壯漢,也可能攔住玩家蒙面殺手盜寶人,如果是后者,把名字保存到name中,同時用look來抓去玩家蒙面殺手或者盜寶人的id
    #tri {@name~((*)~)} {#t- allow;#alarm +2 {#t+ allow};#var id %lower(%1);allow %lower(%1)} {allow}
    //除去壯漢之外的所有人全部放行。有時候玩家可能會瘋狂闖門,為了避免輸入allow命令過多導致守門失敗,在這個trigger觸發之后短暫的關閉2秒再打開。

    #tri {你一腳踢開江湖漢子的尸體,得意地笑了笑。} {#add zhuanghan 1;hp} {守門}
    #tri {你踢了一腳} {#add zhuanghan 1;hp} {守門}
    //守門任務有2類分別是殺江湖漢子和壯漢,做2個trigger來計算殺敵數目

    #tri {你的經驗增加了(*)!} {#var expgrain %1;#var success 成功;#wa 8000;#var zhanghao {};#var mima {};halt;#t- allow;#t- 守門;quit;#alarm +690 {#var zhanghao @zhanghao2;#var mima @mima2;#connect}} {守門}
    //守門成功正常退出游戲之前,為@expgrain,@success賦值,關掉放行和守門2類trigger
    //清空zhanghao,mima阻止自動連線,11分半之后zhanghao,mima的值還原,重新連線

    #tri {你本次守門任務看來做得并不成功!} {#var success 失敗;#wa 8000;#var zhanghao {};#var mima {};halt;#t- allow;#t- 守門;quit;#alarm +690 {#var zhanghao @zhanghao2;#var mima @mima2;#connect}} {守門}
    //這是由于放行次數過多導致守門失敗的trigger,盡管這種情況出現可能性非常小,還是必須要考慮到的,將@success的值改變為"失敗"

    #tri {慢慢地你終于又有了知覺....} {jifaskills;退出} {守門}
    //有放行和受傷自動退出為你的安全作雙重保障,但是仍然不能100%保證安全,這個trigger有必要加上
    //這里jifaskills退出都是alias,退出這個alias用于所有的非正常退出

    #alias jifaskills {jifa dodge juemen-gun;jifa staff juemen-gun;jifa parry juemen-gun}
    //沒什么好說的,死了之后重新jifa
    #alias 退出 {#var success 被干擾;#var zhanghao {};#var mima {};#var time [1440-%ctime-30];halt;quit;#alarm +@time {#var zhanghao @zhanghao2;#var mima @mima2;#connect}}
    //mud中一天是24分鐘,不論你何時退出游戲,只需要等待[1440-%ctime]秒之后就是天亮,這里為了提前半分鐘上線,多減了30.退出前將success的值改為"被干擾".

    步驟四:僅僅有放行還是不夠安全,加入受傷自動退出;如果你已經將挨打的信息都做成了trigger,那么只需要載入就可以了

    #TRIGGER {( 你氣喘噓噓,看起來狀況并不太好。 )} {exert recover} {安全}
    #TRIGGER {( 你似乎十分疲憊,看來需要好好休息了。 )} {exert recover} {安全}
    #TRIGGER {( 你受傷過重,已經有如風中殘燭,隨時都可能斷氣。 )} {halt;#untr +@time;退出} {安全}
    #TRIGGER {( 你搖頭晃腦、歪歪斜斜地站都站不穩,眼看就要倒在地上。 )} {halt;er;#untr +@time;退出} {安全}
    #TRIGGER {( 你看起來已經力不從心了。 )} {halt;#untr +@time;退出} {安全}
    #TRIGGER {( 你受了相當重的傷,只怕會有生命危險。 )} {halt;#untr +@time;退出} {安全}
    //注意:這幾個trigger可能有多個被觸發,而觸發時間略有差異,那么就會同時創建好幾個alarm類型的trigger,因此這里創建新的alarm類型trigger之前用#untr +@time刪除舊的trigger

    步驟五:加入重新連線功能,并且做好守門之前的準備工作,吃飽喝足

    #alias {atconnect} {@zhanghao;@mima;yes} {System}
    #tri {歡迎來到北大俠客行!} {#var success 守門前;#t- 守門;#t- allow;#t- job;#timer off;#untr +@time;#untr +690;w;s;d;out;draw cloth;draw boots;draw head;draw surcoat;draw armor;remove all;wear all;draw staff;draw staff;wield all;#wa 4000;enter;u;hp;tell @zhanghao 已經全副武裝} {準備}
    //連線進入之后要做的事非常多,應該關閉的trigger全部關閉,tick timer關閉,沒有及時刪除的alarm也刪除掉,改變success的值為"守門前"
    //進入游戲領一套新手裝,然后tell自己的id,然后用這句話做觸發
    #tri {*~(@zhanghao~)告訴你:已經全副武裝} {s;s;w;drink;e;n;n;#if (@food<100) {n;n;e;buy ganliang} {#wa 3000;#say 準備就緒}} {準備}
    //判斷食物,小于100就去買干糧,否則準備就緒
    #TRIGGER {你從店小二那里買下了一塊干糧。} {#wa 3000;#10 eat ganliang;w;s;s;#wa 3000;#say 準備就緒} {準備}
    #TRIGGER {窮光蛋,一邊呆著去!} {#wa 3000;w;s;w;qu 30 silver;e;n;e;buy ganliang} {準備}
    #TRIGGER {喲,抱歉啊,我這兒正忙著呢*您請稍候。} {#wa 3000;buy ganliang} {準備}
    #tri {準備就緒} {#wa 2000;enter shudong;say 天堂有路你不走呀;d;3;ne;ne;u;sw;e;sd;e;e;do 3 ne;n;#wa 1500;do 3 e;u;e;do 4 n;#wa 1500;w;d;do 7 n;#timer off;ask duo about 守門} {準備}


    還需要抓取經驗,食物和貢獻度,直接載入就好了
    #TRIGGER {【%s飲水%s】%s(%d)%s/%s%d*【%s經驗%s】%s(%d)} {#var drink %1;#var exp %2} {hp}
    #TRIGGER {【%s食物%s】%s(%d)%s/%s%d*【%s潛能%s】%s(%d)} {#var food %1;#var pot %2} {hp}
    #TRIGGER {貢%s獻%s度:%s&gongxian$} {} {score}


    步驟六:制作按鈕,定義status bar

    #button 0 {修改帳號} {#pr zhanghao "你的帳號";#pr mima "你的密碼" "*";#var zhanghao2 @zhanghao;#var mima2 @mima}
    #button 0 {刪除alarm} {#untr +690;#untr +@time}
    #st {殺死壯漢【@zhuanghan】個   獲得經驗【@expgrain】  當前經驗 【@exp】 【貢獻度@gongxian】  【守門@success】}


    整個制作過程結束

    中級篇已經全部結束,后面主要介紹遍歷。既然是高級篇,怎么也得整些復雜點的東西,恩,更多復雜變態的東西準備奉獻給大家,請繼續關注,謝謝閱讀!




    補充章 Path的用法

    走路一直是機器人制作中的難題,很多人希望學習path的用法,應大家要求補充此章.盡管現在我已經完全不用path了,但是path還是有一些比較好的用途.

    個人認為,462和555版本比較老,Path、Map和DB的功能都不完善,相對于721更屬于半成品.大家如果想用好這3個玩意兒,建議還是用zmud721.因此本章主要以zmud721為準來介紹path.

    另外本人在zmud555和zmud721中使用Path時至今尚被一個問題困擾,如有高手能解決此問題還請賜教.用#slow .2s3e4u完成慢速行走之后,#path顯示出Path Behind:4s6e8u,我所有的方向都一個變倆了.懷疑是slow walk的時候不斷將已走過的命令添加到Path Behind中,同時還在為我錄制路徑,于是每走一步添加2次命令到Path Behind中.這個問題在zmud462中不存在

    這個問題導致#back,#retrace都不好用了.到處尋找設置方法或者命令來解決此問題失敗,憤而完全放棄path另外尋求方法.由于zmud提供的可用于操作path的命令或者函數都比較有限,我感覺還不如直接從path的"本質"出發另外尋求方法取代path,于是就有了高級篇中我要介紹的方法,不過這屬于后話了,下面進入正題.

    Path簡而言之就是預先錄制好的路徑,用法自然就包含2個方面錄制路徑和使用路徑

    補充.1 錄制路徑

    1.1 錄制路徑之前需要預先定義方向.

    zmud一般有預先定義好的幾個標準方向,分別是east,west,north,south,nw,ne,sw,se,u,d.
    這10個標準方向是遠遠不夠的,游戲中的方向非常多,不光有最常見的nu,nd,su,sd,wu,wd,eu,ed,enter,out,還有enter shudong,enter boat,climb,jump,ban stone等等,甚至丐幫暗道那里123456789都是方向.

    點擊菜單View->Directions就可以定義方向了.

    一個方向包含4個要素:Direction,Reverse,Commands,Map

    Direction為方向的代號,只允許單個的符號.比如字母A-Z,!@#$%^&*()[]{}都可以作為方向的代號。由于方向非常的多,你要做的就是在鍵盤上尋找各種各樣的符號作為方向的代號
    Reverse為它的反方向的代號。對于可逆的方向,最好把它的反方向的代號填到這里
    Commands就是方向的命令了,比如e,enter boat,ban stone等等
    Map在畫地圖時才有用,畫地圖時方向就是房間之間的連線。

    并不是所有的方向都要定義之后才能用在path里,比如你也可以把(kill dizi)添加到path里.要注意的是對于沒有定義過的方向需要加上()才能用在path中。這個做法在zmud462和zmud555中行不通,僅在zmud721中有效。

    注:你不僅可以把方向添加到path里,你也可以把諸如(#say 遍歷結束)(#say 需要坐船)這樣的命令添加到path中,然后通過#say顯示的內容制作trigger來坐船或者返回。你還可以將一系列連續的命令包括變量賦值等待建立trigger等都作為一個方向加入,比如(#var xxx xxx;#wa 3000;#tri {} {}),類似這樣的技巧請開動腦筋多多運用

    1.2 方向定義好之后,錄制路徑就很簡單了。你可以有2種方法錄制路徑,推薦使用方法B

    A:輸入#mark命令,然后就在游戲里走路吧,用#path可以查看錄制情況。把你要錄制的路徑走完,用#path查看確認無誤,就可以用#path pathname保存起來了,pathname就是你要保存路徑的名字。

    B:點擊菜單Actions->Record Path,彈出一個小窗口,不用管它,接著在游戲里走路就可以了,你走過的方向都以代號的形式出現在那個彈出的小窗口內,路徑走完之后點擊保存就可以了。

    實例一:#path aaa 3n(kill dizi)2e
    //這里直接用#path命令新建一個path,path名為aaa,路徑是3n2e,表示n;n;n;kill dizi;e;e
    //用這種方式新建的path與錄制制作的path完全一樣

    實例二:#path .fff .3n(kill dizi)2e
    //跟上例完全一樣,只不過path前面加了一個".",表示該path的路徑
    //為什么這里可以多余的加個"."?其實你要弄清楚path其實就是特殊的alias,當你在命令欄輸入.fff或者.3n(kill dizi)2e的時候,命令{n;n;n;kill dizi;e;e}就發送出去了,就好像alias一樣。這個是path的快速行走的用法。

    補充.2 使用路徑

    2.1 快速行走

    假設已經將路徑9n9e9n9e保存到fff中,或者直接在命令欄輸入#path fff 9n9e9n9e
    實例三:輸入命令.fff或者輸入.9n9e9n9e
    //這2個命令效果完全一樣,都是完成了一次快速行走,36個方向命令按順序同時執行

    實例四:上面例子中快速行走命令太多游戲拒絕執行
    #direction q "#wa 3000"
    //將等待3秒定義為一個方向
    #path fff 9nq9eq9nq9e
    //每9個命令之間加入方向q
    .fff
    //作用等同與在命令欄輸入#9 n;#wa 3000;#9 e;#wa 3000;#9 n;#wa 3000;#9 e
    //對于較長的路徑,如果你想快速行走,定義路徑的時候要采取本例做法
    //當然你也可以不定義方向q,而將(#wa 3000)添加到fff中

    2.2 慢速行走

    主要用于搜索npc,相關命令#stop,#step,#back,#retrace,#ok,#slow

    用之前請參考下圖設置,將勾勾去掉,timeout value的值根據網速修改,當你慢速行走時,每個方向命令的間隔時間就是這個值,單位為毫秒。


    ZMUD機器人制作_原著:tangguo(下) - icebergzx - icebergzx的博客
    新建位圖圖像 (3).bmp (667.55 KB)
    2009-7-28 05:46 AM



    #path fff 3n2e
    #slow .3n2e或者#slow 3n2e或者#slow fff
    //慢速行走3n2e
    #pause
    //#pause表示行走是成功的并且暫停慢速行走,當你找到npc用這個命令停止。
    #stop
    //很多人不知道#stop和#pause的區別,#stop表示行走不成功并且停止慢速行走,一般用于被npc擋住或者被系統跘了一下
    #step
    //#step繼續慢速行走。如果你是用#stop停止的,這個命令會從剛才不成功的那步開始直至走完;如果你是用#pause停止的,這個命令會從下一步開始直至走完
    #ok
    //確認行走成功
    #back
    //退回一步,網速不好時你會走過頭,用這個命令一步步往回搜索吧,也可以用這個慢慢的回家
    #retrace fff
    //逆向快速行走。fff的路徑是3n2e,此命令相當于w;w;s;s;s,幫助你回家
    //此命令缺點太多。如果路徑包含有非方向,例如(enter shudong) (#wa 3000),行走到這些地方就會中止
    //如果#retrace不指定路徑單獨使用,則逆向行走當前路徑。用#path查看,逆向行走時針對的當前路徑內容為Path Behind.

    這些命令主要用在你的trigger中,被npc或者門擋住你可以#stop然后殺npc或者開門,殺掉npc之后你可以#step走完剩下的路徑。當然你也可以直接把(kill npc)或者(open dor)添加到路徑里。搜索到盜寶人你可以#pause然后殺掉,任務完成之后你可以#back或者#retrace,也可以#step繼續走剩下的。

    注一:path只是機器人的腳,trigger才是機器人的靈魂。你制作了很多路徑,也需要很多trigger來使用這些路徑。trigger做的不好,機器人就會中斷。游戲中有很多地方都是制作機器人的障礙,迷宮船擋路npc沙漠有busy的方向(例如殺手幫的臺階)需要跟npc對話的地方(例如ask chuan fu about 出海或者answer 送信)等等,你需要用trigger來解決這些障礙。從制作路徑到制作trigger,整個過程需要很多時間才能完成一個遍歷機器人,祝你制作愉快,嘿嘿!

    補充.3 Path的"本質"

    zmud721中有2個函數很好的詮釋了path的本質,可惜的是zmud462和zmud555沒有這2個函數

    在第5章結尾我提到過%ailas()這個函數,作用是將一個alias轉化為string
    在本章的實例二中說過,其實path就是alias詳情可參閱#help.因此我們可以把任意一個path轉化為string

    實例五:#path fff 3n2e(enter dong)d
    #say %alias(fff)
    顯示為3n2e(enter dong)d

    zmud721中才有的2個函數%pathexpand()和%pathcompress()
    %pathexpand()和%pathcompress()的參數都不能是path名字或者alias名字,跟%alias()不一樣,需要將path名字進行轉化才能用到%pathexpand()中。

    #path fff 3n2ed
    實例六:#say %pathexpand(%alias(fff))
    //顯示為n|n|n|e|e|d
    實例七:#say %pathcompress(n|n|n|e|e|d)
    #path ggg %pathcompress(n|n|n|e|e|d)
    //顯示為.3n2ed,注意"."
    實例八:#path fff 3n2ed;#var fff "n|n|n|e|e|d"
    #if (@fff=%pathexpand(%alias(fff))) {#say yes} {#say no}
    //顯示為yes
    #say %format(%alias(fff))
    //顯示為n|n|n|e|e|d

    注二:通過實例6,7,8會發現path其實可以與list類型變量相互轉化。基于這點,我考慮放棄path而轉用list類型變量。path是zmud提供的一個工具,我覺得這個工具功能不完善,定義方向太麻煩,使用起來不靈活,這些也是有些人覺得path不好用的原因。

    在第6章結尾曾經說過,第6,7兩章是所有章節中最重要的兩章,第七章的重要性可能大家已經感覺到了,在高級篇中我會用list類型變量實現path的全部功能,并且能很輕易做到path做不到的事情,個人覺得list類型變量用起來更加隨心所欲一些。

    高級篇

    第十一章 variable和alias的進一步應用

    以后將主要介紹我在胡一刀和推車機器人中用到的遍歷方法,會出現大量的嵌套alias,也會大量的應用list變量,如果你閱讀的比較吃力,請多翻翻前面的章節.

    閱讀前請確認你已經熟練掌握了以下命令:%item(),%additem(),%delitem(),%numitems(),%ismember(),%dups(),%sort()以及對應的#additem,#delitem,#delnitem

    本章用list變量實現path的全部功能,教你做出屬于自己的遍歷機器人

    有些人回帖說看不懂,補充紫色部分,看不懂的話在zmud里試驗這5個例子吧
    ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    #var abc 20|19|18|17|16|15|14|13|12|11|10|9|8|7|6|5|4|3|2|1
    %item(n,@abc)表示abc的第n項,比如%item(14,@abc)就是7,  %item(2,@abc)是19
    先定義一個變量abc來試驗,下面來遍歷abc這個變量
    A      先遍歷2步
    #alias bianli {#if @n {%item(@n,@abc);#add n -1;bianli}}
    #var n 2
    然后輸入bianlin,結果就是先執行第2項19,n減1,接著執行第1項20,n再減1,執行了這2項之后n已經被減成0了,#if @n判斷不為真,遍歷結束
    要小心,#alias a {north;;#add n -1;a}這個alias是無限次嵌套,永遠不會停止,命令行里輸入a的話在將會死機崩潰,因為沒有語句讓它停止,謹慎!!

    B     遍歷20步
    #alias bianli {#if @n {%item(@n,@abc);#add n -1;bianli}}
    #var n 20
    在命令行里輸入bianli,結果就是1;2;3;4;5;6;7;...;20

    C    每遍歷2步停止1秒,一直遍歷20步
    多用一個命令%mod(),是數學里的模,%mod(@n,10)意思是@n除以10后的余數,如果#var n 18,那么%mod(@n,10)的值是8
    #if (%mod(@n,2)=0) {say n是偶數} {say n是奇數}
    一個判斷n是偶數還是奇數的辦法
    #alias bianli {#if @n {#if (%mod(@n,2)=0) {%item(@n,@abc)} {#wa 1000;%item(@n,@abc)};#add n -1;bianli}}
    #var n 20
    跟例子B不同的是,直接執行第n項被替代成了先判斷奇偶,n是偶數直接執行,n是奇數就wait 1000然后執行
    命令行里輸入bianli,結果等同于#wa 1000;1;2;#wa 1000;3;4;#wa 1000;5;...18;#wa 1000;19;20

    D    執行到第5項的時候中止遍歷,用trigger來讓它繼續
    #var abc 20|19|18|17|kill zhang|15|14|13|12|11|10|9|8|7|6|5|4|3|2|1
    第5項被替換成了kill zhang
    #alias bianli {#if @n {#if (@n=5) {%item(@n,@abc)} {%item(@n,@abc);bianli};#add n -1}}
    #var n 20
    #trigger 張死了 {bianli}
    命令行里輸入bianli,結果就是1;2;3;4;5;......;13;14;15;kill zhang,等張死了,繼續17;18;19;20

    E   執行到第5項時等待10秒再執行,其他的直接執行,用這個方法來坐船
    #var abc 20|19|18|17|16|enter boat|14|13|12|11|10|9|8|7|6|5|4|3|2|1
    第6項被替換成了enter boat
    #alias bianli {#if @n {#if (@n=5) {#wait 10000;%item(@n,@abc)} {%item(@n,@abc)};#add n -1;bianli}}
    #var n 20
    命令行里輸入bianli,結果就是1;2;3;4;...;13;14;enter boat;#wait 10000;16;17;18;19;20


    ---------------------------------------------------------------------------------------------------------------------------------------------------------


    11.1 給出一個路徑.從ct出發到無量山溜一圈回到ct的所有方向組成的list,用一個變量保存起來.

    #var wuliang {s|s|s|s|s|s|s|s|s|sw|w|w|nw|w|e|sw|sw|sw|n|n|n|s|s|s|s|sw|sw|ne|ne|n|sw|wu|nu|sd|ed|nw|n|s|nu|nu|nu|climb stiff|up|climb yafeng|e|se|sw|n|s|sw|sw|sw|s|e|n|w|n|w|e|s|ne|ne|ne|s|push stone|s|e|ed|ed|push men|e|n|s|e|w|s|out|guo qiao|d|s|se|ne|ne|ne|ne|se|e|e|ne|n|n|n|n|n|n|n|n|n}

    這個路徑的制作方法可以模擬path路徑錄制的方法,下一章再詳細講,先不管這個.

    比path路徑要靈活的是,不需要定義方向,可以將任何命令添加到list里面去.這里說的任何命令,不光是上面@wuliang包含的常規方向和非常規方向,類似#var xxx xxx,#wa xxx,#alarm xxx,tell @myid xxx都可以添加進去,甚至可以自己定義復雜的alias和function,然后把alias和function添加進去.總之,沒有什么是不能添加的,而這種靈活性將使你的遍歷非常強大.

    11.2 快速行走

    快速行走方法一:
    #forall @wuliang {%i}
    //這就已經完全模擬了path的快速行走,但是這個模擬跟path的快速行走一樣太簡單了.上面的路徑總共包含98個item,命令輸入過多游戲拒絕執行.

    快速行走方法二:
    #alias bianli {#if (@step<=@steps) {#if (%mod(@step,@bushu)=0) {#wa @wait;halt;%item(@area,@step);#add step 1;bianli} {%item(@area,@step);#add step 1;bianli}}}
    //一個簡單的嵌套,@step表示你的當前步數,@steps表示要走完的步數,因此@step<=@steps作為嵌套結束的判斷條件.這個alias能讓你從第@step-1步開始走完第@steps步.路徑保存在@area中
    //當@step<=@steps時,判斷%mod(@step,@bushu)是否為0:如果為0,休息一會#wa @wait之后走一步%item(@area,@step),當前步數加1進入下一次嵌套;否則,直接走一步,當前步數加1進入下一次嵌套
    //這里@bushu,@wait用來控制行走的快慢,每次連續走@bushu步就停@wait秒,然后接著走.這里不用具體數字而是用變量來控制行走快慢,可以讓你更方便的調節行走速度.

    #alarm 00:30:00 {#var bushu 15;#var wait 1000}
    #alarm 12:30:00 {#var bushu 8;#var wait 1500}

    //用這2個trigger來自動調節行走速度,晚上人少網絡流暢,可以走快些,中午過后網絡不好可以走慢些.當然自動調整不滿意,你可以隨時手動調整,或者制作按鈕來改變@bushu和@wait的值

    無量山的快速行走
    #alias 無量山 {#var step 1;#var area @wuliang;#var steps %numitems(@area);bianli}
    //將@wuliang保存在@area中,@step值為1表示從第0步開始,@steps值為所有item的個數,表示走完全程
    //你只需要在命令行輸入"無量山"即可完成無量山的快速行走

    注一:可以看到這個方法和path相比強大的多,你可以10步一走,也可以2步一走,也可以走一步停一下(這就已經是慢速行走了),完全可以隨心所欲.

    細心的同學會發現climb stiff和guo qiao有busy,上述方法有問題.沒錯,下面做出修改.

    #alias bianli {#if (@step<=@steps) {#if %ismember(@step,@busy) {#wa @time;halt;%item(@area,@step);#add step 1;bianli} {{#if (%mod(@step,@bushu)=0) {#wa @wait;halt;%item(@area,@step);#add step 1;bianli} {%item(@area,@step);#add step 1;bianli}}}}
    //與前面唯一不同的是先判斷當前步數是否屬于有busy的步數,如果是等待@time,接著走;否則跟上面完全一樣
    //有busy的步數保存在變量@busy中

    #alias 無量山 {#var step 1;#var area @wuliang;#var steps %numitems(@area);#var time 30000;#var busy 43|79;bianli}
    //手動輸入#show %ismember("guo qiao",@wuliang)%ismember("climb stiff",@wuliang)顯示為7842,所以需要在"無量山"這個alias中添加#var busy 43|79,并且,#var time 30000將busy的等待時間設置為30秒
    //如果你的路徑中沒有這類具有busy的方向,比如丐幫洛陽,你只需要#var busy {}
    //climb stiff和guo qiao命令輸入之后都將等待30秒輸入下一個命令,可以用下面的trigger調整

    #tri {突然你突然腳下踏了個空,向下一滑,身子登時墮下了去。} {#wa 1}
    #tri {你終于一步步的終于挨到了橋頭} {#wa 1}
    //這里利用#wa命令的缺點,用#wa 1來替代#wa 30000,起到加速的作用,不會真的等待30秒那么長的時間,也絕不會等待時間太短,就是剛剛好

    事實上,所有的方向無非就是有busy沒busy兩種(殺擋路npc也可以算是有busy的方向的一種),因此用上面的方法已經可以做出一個跟dammya完全一樣的遍歷機器人了,還缺少的只是下一章的路徑錄制方法.

    注二:這里將所有的有busy的方向統一處理.事實上,你可以做得更精細一些,分成幾類來處理效果會更好

    11.3 慢速行走

    慢速行走方法一:把上面的@bushu值改為1就是慢速行走了,不過這種行走無法停下來,#stop,#step,#back都無法實現

    慢速行走方法二:
    #alias bianli2 {#if (@step<=@steps) {#if (@finddbr=0) {#wa @time;halt;%item(@area,@step);#add step 1;bianli2}}
    //行走前先判斷@finddbr的值,如果為0,表示沒有找到dbr,走下一步;否則嵌套結束,停止行走
    //不管當前步數有沒有busy,統一等待@time,@time在下面的alias中設置為30000

    #alias 無量山2 {#var finddbr 0;#var step 1;#var area @wuliang;#var steps %numitems(@area);#var time 30000;#var busy 43|79;bianli2}
    //慢速行走之前將@finddbr的值改變為0,表示沒有找到dbr

    剩下的就是做trigger了

    #tri {這里明顯的出口是} {#if %ismember(@step,@busy) {#wa 1}}
    //成功走出了一步.如果下一步沒有busy,用#wa 1加速;否則什么都不做

    注意;在無量山,成功的push stone,push men都有相應的描述,都表示成功走出一步,因此都需要做trigger加速.不加速的話也只是浪費點時間而已

    #tri {突然你突然腳下踏了個空,向下一滑,身子登時墮下了去。} {#wa 1}
    #tri {你終于一步步的終于挨到了橋頭} {#wa 1}

    //有busy的步數用專門的trigger來加速

    #tri {盜 寶 人*@dbrname~(@dbr~)} {#var finddbr 1;#wa 1;大開殺戒}
    //找到dbr之后先改變finddbr的值,再加速然后開殺

    任務完成返回:

    順向返回:沒什么好說的,很簡單的返回方法了

    逆向返回:
    #var fangxiang {n|e|nw|ne|nu|nd|eu|ed|u|enter|s|w|se|sw|sd|su|wd|wu|d|out}
    #var fangxiangb {s|w|se|sw|sd|su|wd|wu|d|out|n|e|nw|ne|nu|nd|eu|ed|u|enter}
    //2個變量對應的方向正好相反

    依次取出前一個方向,通過@fangxiang獲得序號,通過序號在@fangxiangb里查找反方向,行走的具體做法我就不寫了

    注三:無量山這里只能順向返回.你也可以在返回時加入自己的判斷,從而把順向返回和逆向返回結合起來,從而將效率最大化.

    注四:上面只給出了標準方向的反方向獲取方法.對于非標準且可逆的方向,都可以相應的添加到上面的@fangxiang,@fangxiangb中.

    注五:丐幫暗道2的反方向是sw,但是sw的反方向可能是2,也可能是ne,解決的辦法也有很多.實在想不出什么好辦法,你也可以為2和se制作專門的alias,然后將alias作為2和se的替代方向來使用.

    注五:對于其他的路徑,有的需要開門,有的需要給錢老板gold,這些命令都可以作為方向添加到路徑里去,慢速行走時作好相應的trigger來加速,不一一細說了

    注六:行走失敗時,無非是被系統跘住或者被自動叫殺的npc糾纏住或者busy還沒結束不能移動,此時當前步數減一繼續行走,不詳細給出相應的trigger了.

    注七:此方向效率比path高很多倍,慢速行走每步之間的間隔幾乎沒有.如果你用path來做遍歷,至少要設置等半秒吧,一個大型區域比如丐幫洛陽凌霄都有幾百步,需要幾分鐘才能走完,一個胡一刀任務的完成時間通常就會超過15分鐘.

    注八:上面的方法已經實現了#step,#stop,#back,#pause等功能.事實上實現的方法有很多種,上面的方法也肯定不會是最佳的一種.

    注九:還是那句話,遍歷做起來工作量很大,需要很多trigger來使用路徑.

    下一章將介紹路徑的制作方法,以及完成更多path根本做不到的事情.
    第十二章 遍歷實現的一種方法

    本章繼續介紹遍歷的制作.先給出路徑的制作方法,然后將遍歷以及返回的方式多樣化.

    注:在機器人制作過程中,并不一定要用到本章及隨后兩章介紹的所有方法。一切以你給機器人設定的功能為基礎,兼顧穩定效率簡潔選擇合適的方法。如果你覺得有些方法實現起來太復雜,你可以簡化它,也可以不用理會它或者改進它。本章及隨后兩章僅供參考

    12.1 路徑的制作

    一個list變量如果手動添加item以及間隔符號"|",不僅浪費時間,也容易出錯,參考path的制作過程,制作下面的alias

    制作路徑之前需要將相關變量清空,為此制作一個按鈕

    #button 0 #mark {#var area {};#var busy {}}
    //這里的按鈕相當于path路徑制作#mark的功能,所以取名為#mark,路徑臨時保存在@area中,busy方向的序號保存在@busy中

    如果你把有busy的方向分得更精細,你還需要更多的變量來保存這些方向的序號,請一并清空

    #alias g {%1;#var area %additem(%1,@area);#say @area}
    //定義一個alias,在走路的同時將方向添加到@area中,并且顯示出@area的內容

    有了這個alias,你只需要用g s,g e,g "enter boat",g "aliasname"這些命令在游戲里走路就可以了.在你走路的同時,s,e,enter boat,aliasname都自動添加到變量@area中了,并且同步顯示出變量@area的內容.

    #var pathname @area;#var area {}
    //路徑制作完畢之后,保存到pathname中,同時清空@area準備下一條路徑的制作

    再用下面的按鈕輔助就更省事了
    #button 0 記錄busy方向 {#var busy %additem(%numitems(@area),@busy)}
    #button 0 顯示busy方向 {#say @busy}
    //碰到有busy的方向,輕輕點擊一下第一個按鈕.等整個路徑走完之后點擊第二個按鈕就可以知道哪些步數有busy了.在保存路徑的同時順便將路徑的遍歷方法一并寫好.

    注一:你可以邊走路邊制作trigger,例如上一章中快速行走慢速行走需要的trigger都在走路的時候就作好,將會節約你大量的時間

    注二:整個路徑走完,與該路徑有關的alias,variable,trigger就已經全部制作好了,當場測試一遍確認無誤就可以開始下一條路徑的制作!

    為了讓遍歷的方式更加多樣化,在制作路徑的時候可以記錄更多的內容,本章的第2小節將會用到.
    #button 0 記錄關鍵地點 {#var room %additem(%prompt("","關鍵地點"),@room);#var roomnum %additem(%numitems(@area),@roomnum)}
    #button 0 記錄關鍵npc {#var npc %additem(%prompt("","關鍵npc"),@npc);#var npcnum %additem(%numitems(@area),@npcnum)}
    //用到函數%prompt(),跟#pr類似,不過這個函數不會將你輸入的內容賦值給變量,而是將你輸入的內容作為函數的值.
    //在錄制路徑的時候,點擊這2個按鈕,會彈出一個對話框,讓你輸入"關鍵地點"或者"關鍵npc",你可以為當前地點或者這個房間的npc取一個名字,名字被保存在@room或@npc中,當前地點的編號自動保存在@roomnum或@npcnum中
    //比如你在發呆室,可以點第一個按鈕輸入"修理裝備"來保存修理裝備的地點
    //你在胡一刀那里,可以點第二個按鈕輸入"老胡"來保存胡一刀的位置

    12.2 遍歷的多樣化

    步驟一:請大家自己動手完成這一步,用上一章的方法,寫好針對@area的遍歷,包括順向快速行走,逆向快速行走,順向慢速行走,逆向慢速行走

    為了下面敘述方便,假設你已經完成了上一步,順向快速行走的alias是fastwalk,逆向快速行走是fastwalkb,順向慢速行走是slowwalk,逆向慢速行走是slowwalkb

    這些alias中用到的變量有:
    @step--表示你的當前位置,在起點位置為1,在終點位置為%numitems(@area)+1,依次類推
    @steps--表示行走的終點位置,即順(逆)向快(慢)行走停止之后你所在位置應該為@steps
    @busy--有busy的方向在路徑中的編號

    注三:有興趣動手完成這個步驟的同學,可以在做好這4個alias之后測試一下,一定要確保行走停止之后@steps和@step的意義與上面所述相同,如有差異請稍作修改。對list變量的遍歷感到陌生的同學,這會是一個很好的鍛煉

    步驟二:用遍歷行走的時候,不管你是什么原因停了下來,你的trigger都應該保證@step的值是你當前所在的位置

    注四:由于某些trigger無法判斷的原因,導致@step中的值不是你當前所在的位置,我們稱這一現象為"亂入",這在胡一刀和護鏢任務中都可能發生,解決辦法留待后面2章介紹,現在暫時不管這個。

    步驟三:制作功能更強大的遍歷alias

    #alias fastgoto {#if %isnumber(%1) {#var steps %1};#if %ismember(%1,@room) {#var steps %item(@roomnum,%ismember(%1,@room))};#if %ismember(%1,@npc) {#var steps %item(@npcnum,%ismember(%1,@npc))};#if (@steps>@step) {fastwalk} {fastwalkb}}
    //快速行走的alias,擁有一個參數,這個參數可以是一個數字,也可以是房間的名字,也可以是npc的名字
    //%isnumber()這個函數可以判斷是否為數字,如果是直接賦值給@steps
    //如果參數時房間的名字或者npc的名字,將房間的編號賦值給@stpes
    //通過比較@step和@steps的大小關系選擇是順向行走還是逆向行走

    fastgoto 修理裝備:從任意地點走到發呆室修裝備
    fastgoto 老胡從任意地點走到胡一刀那里
    fastgoto [@step+1]:從任意地點順向走一步
    fastgoto [@step-1]:從任意地點逆向走一步
    fastgoto 1或者fastgoto [%numitems(@area)+1]:從任意地點走到起點或者終點
    fastgoto %1:如果[email=%1=@step]%1=@step[/email],停留在原地

    可以看到這個遍歷功能很多,如果你在制作路徑的時候記錄的npc或者房間名稱很豐富,你的行走將隨心所欲

    此命令有2個缺點:
    (1)無法判斷路徑是否可逆
    (2)路徑是全區域的遍歷,因此行走過程中可能要走很多不必要的步數

    注五:下一章會修改這個alias,將所有的情況用一個alias來完成.如果可逆就逆向行走至目的地,如果不可逆就順向返回起點再順向行走至目的地;并且在不需要遍歷的時候將路徑簡化,只走最短路線

    慢速行走完全類似
    #alias slowgoto {#if %isnumber(%1) {#var steps %1};#if %ismember(%1,@room) {#var steps %item(@roomnum,%ismember(%1,@room))};#if %ismember(%1,@npc) {#var steps %item(@npcnum,%ismember(%1,@npc))};#if (@steps>@step) {slowwalk} {slowwalkb}}

    12.3 遍歷結束方式的改進

    拿上一章的兩個例子來說

    快速行走;#alias bianli {#if (@step<=@steps) {#if (%mod(@step,@bushu)=0) {#wa @wait;halt;%item(@area,@step);#add step 1;bianli} {%item(@area,@step);#add step 1;bianli}}}
    慢速行走;#alias bianli2 {#if (@step<=@steps) {#if (@finddbr=0) {#wa @time;halt;%item(@area,@step);#add step 1;bianli2}}

    基本上遍歷就靠這這2個alias完成,但是遍歷的目的不一樣,有可能是為了返回,有可能是為了逆向或者順向一步步搜索,遍歷結束也分達到目的或者未達到目的2種情況,導致判斷下一步行動的trigger作起來并不容易,甚至根本無法判斷遍歷是否已經結束,因此要作些改進.

    改進的快速行走:#alias bianli {#if (@step<=@steps) {#if (%mod(@step,@bushu)=0) {#wa @wait;halt;%item(@area,@step);#add step 1;bianli} {%item(@area,@step);#add step 1;bianli}} {#exe @action}}
    //只在嵌套結束之后加上#exe @action

    你可以在各種trigger中先改變@action的值再使用遍歷,比如
    #var action "tell @zhanghao 出發尋找盜寶人"
    #var action "tell @zhanghao 返回結束"
    #var action "#say 開始逆向搜索"
    //通過這些結束信號作trigger就容易多了。

    有的區域過于龐大,如果做成一個路徑的話,像洛陽丐幫北京等區域都會超過300步,整個遍歷下來非常費時間影響效率,因此最好分成幾個小路徑。
    這就涉及到多個路徑的連續遍歷,如果遍歷目的達到,直接返回;否則繼續遍歷下一個路徑。多個路徑連續遍歷的具體方法將在第14章中介紹。

    本章結束,下章介紹路徑的逆轉和簡化,簡單說說胡一刀和推車全自動實現的障礙及應對辦法

    第十三章 胡一刀和護鏢全自動分析

    繼續前一章關于遍歷的一些技巧,有的時候需要獲得一個路徑的返回路徑,有時候需要走最短路線以提高效率,這就是路徑的逆轉和簡化.

    注一:本章的例子如果看不懂,可以將復雜的部分分解成小部分慢慢理解,也可以直接將代碼復制到zmud命令欄中進行試驗驗證.本章就不過于羅嗦注釋了,如果掌握了方法,這些代碼寫起來還是很簡單的.

    13.1 路徑的逆轉和簡化

    逆轉就是將路徑倒過來,并且每個方向都替換成逆方向.

    #var fangxiang {n|e|nw|ne|nu|nd|eu|ed|u|enter|s|w|se|sw|sd|su|wd|wu|d|out|open door}
    #var fangxiangb {s|w|se|sw|sd|su|wd|wu|d|out|n|e|nw|ne|nu|nd|eu|ed|u|enter|open door}
    //這2個變量只保存了常規的可逆的方向,如有其它可逆的方向都可以添加進去,只需要注意2個變量對應的方向正好相反就可以了
    //不屬于上述2個變量中的方向,默認為不可逆.路徑不可逆時只能繼續遍歷到終點

    #alias reverse2 {#if (@step>0) {#if %ismember(%item(@path,@step),@fangxiang) {#var path2 %additem(%item(@fangxiangb,%ismember(%item(@path,@step),@fangxiang)),@path2);#add step -1;reverse2} {#say 路徑不可逆}} {#var path @path2;#say 路徑逆轉完畢}}
    //逆轉路徑的alias寫起來很簡單,將你要逆轉的路徑保存在@path中,從@path的最后一個item開始依次取出每個方向,如果可逆則獲得反方向添加到@path2中,否則"#say 路徑不可逆"并且終止逆轉

    #alias reverse {#var path2 {},#var step %numitems(@path),reverse2}
    //初始化變量@path2和@step,然后啟動reverse2開始逆轉

    上面2個alias就完成了逆轉,逆轉的對象是@path.

    使用逆轉的方法:
    要逆轉某個路徑@abc,輸入命令#var path @abc;reverse.如果路徑可逆,則將逆轉后的路徑保存在@path中并且輸出信息"路徑逆轉完畢";否則輸出信息"路徑不可逆"并且@path中的內容不變.你可以分別用"路徑逆轉完畢"和"路徑不可逆"制作trigger繼續下一步行動.

    簡化就是判斷路徑中相鄰的2個方向是否互為可逆,如果是則同時刪除.

    實例:
    #var xyscq {n|n|w|w|w|e|e|n|s|s|e|w|n|e|n|n|n|s|s|s|e|n|w|e|s|s|n|e|s|e|s|n|w|nw|se|w|e|s|e|w|w|e|s|n|n|n|e|n|s|s|n|e|n|s|s|n|e|w|w|w|n|se|e|w|nw|w|e|e|u|d|w|n|w|u|d|e|e|u|e|w|u|d|d|w|n|n|n|nw|n|s|se|n|n|n|n|過河|n|e|w|w|u|d|e|n|n|w|nu|sd|w|nu|sd|e|e|ne|ne|n|s|sw|sw|n|n|e|w|n|n|n|n|w|e|n|w|e|n|w|e|e|w|n|e|n|s|e|n|s|e|w|w|w|n|e|w|n|e|w|w|e|n|s|s|s|w|n|s|s|n|w|s|n|n|s|w|w|w|w|e|e|e|e|e|e}
    //這個變量中保存了從林震南那里一直到襄陽史春秋附近的遍歷路徑,沿途各個分叉口全都遍歷到了

    搜尋伙計可以遍歷這個路徑,但是返回仍然用這個路徑則會浪費很多時間,需要寫一個alias獲得從起點到終點的最短路徑

    #alias jianhua2 {#if (@step>=%numitems(@path)) {#say 路徑簡化完畢} {#var num1 %ismember(%item(@path,@step),@fangxiang);#math step2 @step+1;#var num2 %ismember(%item(@path,@step2),@fangxiangb);#if (@num1=0) {#add step 1;jianhua2} {#if (@num1=@num2) {#delnitem path @step;#delnitem path @step;#add step -1;jianhua2} {#add step 1;jianhua2}}}}
    #alias jianhua {#var step 1;jianhua2}
    //這2個alias就完成了路徑@path的簡化,簡化結束后輸出信息"路徑簡化完畢"

    #var path @xyscq;jianhua
    //將@xyscq賦值給@path,然后簡化它

    #say @path
    //顯示為n|n|e|e|n|n|n|n|n|n|n|n|n|過河|n|n|n|n|n|n|n|n|n|n|n|n,得到從林震南那里到襄陽城中心的最短路徑

    注二:如果要獲得從起點到當前位置再到終點的最短路徑,只需要對上述方法進行修改即可.一個比較簡單的方法就是:先將路徑@path中當前位置的方向替換成myaddress,并且將該方向保存到@myaddress中,由于"myaddress"不可逆,因此簡化的時候不會刪除myaddress.簡化結束后將路徑@path中的myaddress還原或者新建一個alias:#alias myaddress {@myaddress}都可以。

    注三:護鏢任務的路徑,路徑都很簡單,方向都很規范,用逆轉和簡化的時候不需要考慮可逆不可逆的問題。胡一刀的路徑就要復雜些,很多特殊的方向和不可逆的方向,還有很多有時候可逆有時候不可逆的方向,在使用逆轉和簡化時就需要特別注意了,一般主要用于洛陽北京襄陽丐幫揚州這類路徑較長并且方向規范的區域;如果硬要用于胡一刀的全部路徑,也不是不可以,只是制作路徑和定義可逆方向的時候復雜很多罷了。

    注四:逆轉和簡化只是2個將路徑"變形"的方法,每個人寫機器人的方法都不一樣,請參考使用。對路徑的"變形"使用不僅僅只有這2個方法,如有需要,完全可以自己寫一些其它的alias來使用路徑。用list類型變量來制作遍歷的優點就在這里,一個路徑拿來,想怎么玩就怎么玩,這是path辦不到的,只是要求能熟練的應用和list類型變量和嵌套罷了。

    13.2 胡一刀和護鏢全自動

    對于這種復雜的大型機器人,需要克服的障礙很多,運行過程中各種意外會經常出現,想一次性達到完美是不可能的。寫機器人之前,不妨先大體考慮下主要會出現哪些問題,哪些是容易解決的,哪些是比較困難的,哪些是不可能解決的,該放棄的就適當放棄,比較麻煩的地方以后慢慢完善,畢竟最終的目的是要全自動。

    搜索盜寶人

    可以用慢速行走,第11章中的方法就可以完成。用這個方法時建議set brief 2,"這里明顯的出口是 south 和 out。"類似這樣的信息會出現在盜寶人的后面,用這些信息作trigger可以正確判斷是繼續下一步還是停下殺盜寶人。殺完之后的采用順向返回,為了提高效率,返回時最好簡化路徑。不必追求完美的簡化獲得最短路徑,非標準的方向都默認為不可逆,只簡化掉標準的方向即可。相比完美的簡化,僅僅就是多浪費0-4秒鐘在返回的路上,但是實現起來卻容易得多。

    也可以用快速行走,優點就是搜索盜寶人的速度更快。由于主id用快速行走來搜索,肯定會走過頭的,路過npc的時候也會導致盜寶人叫殺,盜寶人叫殺之后就會亂跑,因此推薦用大米快速搜索。快速搜索盜寶人需要通過數房間名出現的次數或者數"這里明顯的出口是 south 和 out。"這類信息出現的次數得到盜寶人在路徑中位置。如果坐船殺擋路npc或者開門也是路徑中的方向的話,數數的時候也要作調整,一定要確保最后得到的結果是正確的。主id通過盜寶人的位置快速行走到盜寶人那里就可以了。這種方法也有缺點,被系統跘住或者被主動叫殺的路人甲糾纏住這類情況總是會發生的,如果是大米退出重新搜索就可以了;如果是主id,可以讓大米再搜索一次,同時搜索自己和盜寶人。

    對于第十一章中給出的快速行走也可以稍作修改

    #alias bianli {#if (@step<=@steps) {#if %ismember(@step,@busy) {#wa @time;halt;%item(@area,@step);#add step 1;bianli} {{#if (%mod(@step,@bushu)=0) {#wa @wait;tell @zhanghao 繼續快速行走} {%item(@area,@step);#add step 1;bianli}}}}
    #tri {你告訴*:繼續快速行走} {%item(@area,@step);#add step 1;bianli}
    //對%mod(@step,@bushu)=0的情況作了修改,此時嵌套暫時結束并且tell自己一句話,用這句話作觸發繼續嵌套,可以完全避免網絡lag時命令堆積過多服務器拒絕執行的情況。

    有些迷宮也是可以搜索的,比如大沙漠
    #var shamo {w|heshui|w|heshui|w|heshui|s|heshui|w|heshui|w|heshui|w|heshui|s|heshui|w|heshui|w|heshui|w|heshui|w|heshui|w|heshui|s|heshui|w|heshui|w|heshui}
    #alias heshui {#10 drink jiudai}
    如果遍歷星宿沒有找到盜寶人,可以用慢速行走遍歷大沙漠,酒袋里裝滿水去沙漠起點就可以開始遍歷了,每個"heshui"方向都有busy。類似這些地方,初次制作機器人的時候最好直接放棄,以后再慢慢修改添加完善。

    護鏢

    這個就比較簡單了。由于路徑都很簡單,只包含標準方向,可以直接從林震南那里遍歷搜索店鋪伙計,也可以先將鏢車推到伙計附近的某個固定地點,如果沿途沒有看到店鋪伙計,再搜索伙計附近的小型區域。搜索結束后將路徑中伙計后面的方向全部刪除,剩余部分簡化就得到從鏢車到伙計的最短路徑,推過去就可以了。無論是大米搜索還是主id搜索,也無論是大米貼身跟隨還是呆在家里,實現起來都沒有難度。

    逆轉和簡化在護鏢機器人中用起來很方便,沒有什么需要注意的地方。并且路徑也不用做成封閉式的(即從起點出發溜一圈回到起點),可以自己寫一個返回alias,從當前位置依次往前執行每個方向的反方向直到回到起點。無論在什么地方,只要沒有迷路(此時@step的值就是當前所處位置在路徑中的編號),將@step后面的方向刪除,逆轉簡化順序快速行走就返回了。

    推車子的時候使用的路徑也是類似{e|e|e|n|n|ne}這樣的list變量,定義2個變量
    #var fangxiang {n|e|nw|ne|nu|nd|eu|ed|u|enter|s|w|se|sw|sd|su|wd|wu|d|out}
    #var fangxiang2 {north|east|northwest|northeast|northup|northdown|eastup|eastdown|up|enter|south|west|southeast|southwest|southdown|southup|westdown|westup|down|out}
    用gan che to %item(@fangxiang2,%ismember(%item(@path,@step),@fangxiang))命令推車就可以了

    解決亂入或者迷路的萬能辦法

    無論是護鏢中鏢車被劫匪移位,還是胡一刀中在殺盜寶人的時候盜寶人到處亂跑,或者是其他的意外情況,都導致一個結果,就是@step的值不再是你在路徑中的位置,將你所在區域告訴你的大米,讓他來搜索你,重新更正@step的值或者直接更正路徑都可以。

    要求你有從林震南到店鋪伙計那里的完整路徑,沿途每個分岔口都要覆蓋到,以確保亂入3步以內能被大米搜索到。

    如果亂入到沙漠黑店ct上面的賞月臺這樣的地方,這些位置可能不在路徑中,大米是搜索不到的,尋求大米幫助之前先判斷下地名就可以了,如果是這類地方,推到路徑中之后再向大米發出求助信息。

    對于亂入,一般只是移位1-2步,比較簡單,下章再介紹2個解決辦法供參考。

    注五:大概想到的困難也就這些,如果大家還有什么困難不能解決可以回帖說明,如果我有解決的辦法會修改補充進來。

    注六:建議大家在制作機器人之前不要想將所有困難一網打盡,一次性考慮所有情況只會使你做不下去,你的機器人就夭折了。對于一些細小的地方,該放棄的就放棄掉。比如為了鰲拜下面的密室這一個房間專門寫很多trigger進去搜索是沒有必要的。全自動做任務肯定沒有半自動手動操作的成功率高,但是機器人勝在反應速度比人快,勝在不知疲倦,這些優點也足以忽視它的不足了。先保證機器人的效率和穩定,再慢慢提高機器人完成任務的成功率,對于麻煩的復雜的情況建議暫時做放棄處理,大不了退出重新連線開始下一個任務嘛!

    下一章介紹多個路徑的連續遍歷方法,另外提供2個解決亂入的辦法供大家參考!

    第十四章 實際例子--解決亂入的2種辦法

    鑒于有人給我發消息推薦一些能對string進行操作的函數,本章將刻意使用string相關函數

    14.1 多路徑的連續遍歷

    路徑的分割

    對于大型區域,可以分解成一個個的小型區域,每個小型區域單獨制作路徑,任務需要遍歷大型區域,這就要求能把一連串的路徑銜接起來。如果能實現這種要求,會縮短遍歷時間,從而更加效率。

    為了使銜接更加容易,在制作路徑的時候,對大型區域的分割要合理,否則不光不利于銜接,也不利于返回。

    分割大型區域的方法有很多,這里我介紹我用的一種分割方法供大家參考。

    拿神龍島舉例,整個大型區域的名字定為shenlong,整個區域有坐船有神龍弟子擋路,有個地方能爬懸崖有比較長的busy,全部遍歷比較費時間。

    將它分割成5個小型區域shenlong1,shenlong2,shenlong3,shenlong4,shenlong5

    考慮到神龍島的地形,在峰頂練武場那里開始出現岔路,往左是通往洪教主,往右是通往五龍堂,另外半山腰那里客廳,客廳后面山泉那里能往下爬。

    shenlong1:從ct到峰頂練武場的路徑。ct到塘沽口直接最短路線遍歷過去,從塘沽口開始路徑中要包含路上每個岔路,一直到峰頂練武場停下。此路徑中不包含半山腰客廳已經往下爬懸崖的那部分房間。
    shenlong2:從練武場往五龍堂方向遍歷,然后返回練武場的路徑。
    shenlong3:從練武場往洪教主方向遍歷,然后返回練武場的路徑。
    shenlong4:從練武場往半山腰客廳方向遍歷,然后返回練武場的路徑,包括山泉那里爬懸崖。
    shenlong5:從練武場最短路線返回ct的路徑。

    這種劃分的好處在于,不論在哪個小區域找到了盜寶人,直接跳至最后一段路徑shenlong5,從而最短路線返回。

    注一:有人也許會問,為什么不能找到盜寶人停下,然后原路返回?主要原因在于原路返回需要用到路徑的逆轉和簡化,而逆轉和簡化的運用是有限制的。有的地方是不能原路返回的,比如無量爬上去了就不能往回爬下來。另外比如慕容茶花林是個迷宮,進去和出來的路徑不一樣,路徑的逆轉簡化都不能用。再者,逆轉和簡化的制作難度不比多路徑的連續遍歷簡單,即使無量那里能往回爬下來,也要對climb stiff,climb yafeng這些方向定義逆方向。另外簡化路徑的時候路徑縮短,有busy的方向所在序號都會發生改變,計算起來也比較復雜。

    再在拿慕容來舉例,整個區域可以這樣劃分:
    murong1:從ct到琴韻的碼頭的最短路線
    murong2:從琴韻碼頭cai yanziwu過去,遍歷整個燕子塢cai qinyun回到碼頭
    murong3:從琴韻碼頭cai tingxiang過去,遍歷整個聽香水榭cai qinyun回到碼頭
    murong4:從琴韻碼頭遍歷琴韻然后tan qin,row mantuo在遍歷整個曼陀最后enter boat返回碼頭
    murong5:從碼頭最短路線回到ct

    拿洛陽來舉例,整個區域可以這樣劃分:
    luoyang1:從ct走暗道到洛陽中心廣場,不需要遍歷的部分直接最短路線走過去,凡是盜寶人可能會出現的房間都遍歷到
    luoyang2:從洛陽中心廣場遍歷洛陽北街附近所有房間,回到洛陽中心廣場。
    luoyang3:從洛陽中心廣場往西遍歷包括萬安寺然后返回洛陽中心廣場
    luoyang4:從洛陽中心廣場往東南遍歷包括天地會里面然后返回中心廣場
    luoyang5:從洛陽中心廣場經由渡口方向cross river返回ct

    注二:洛陽這地方著實大了點,盜寶人會出現在渡口附近甚至包括漢水南岸,也會出現在萬安寺天地會這些地方,也會出現在由暗道去洛陽的路上,洛陽城本身都夠大。整個區域如果做成1條路徑會包含300多個方向,整個走完即使是快速行走也要1分鐘左右。如果你采用先搜索一次定位,再遍歷一次擊殺返回這樣的方法來做機器人,僅僅是走路的時間差不多都要1分半了,這還不算你殺盜寶人的時間。
    采用多路徑的連續遍歷,平均節約至少60%的遍歷時間,如果你的遍歷做的好,你會發現機器人速度比傳音搜魂和求助vast還快。

    注三:天龍寺這個區域,盜寶人可能會出現在無量山的懸崖峭壁上。本人做胡一刀任務以來,遇到過幾十次吧。如果采用1條路徑來遍歷整個天龍寺,這個懸崖峭壁就是個雞肋,路徑中包含它,意味著每次都要爬懸崖過善人渡,遍歷時間延長3倍不止,如果不包含它,本次胡一刀任務直接失敗。

    這里分割區域就很好的解決了這個問題,路徑分割成4個小區域,去天龍寺,遍歷天龍寺,爬懸崖無量山走一圈,返回中央廣場。
    如果在第2個區域找到盜寶人,直接跳至第4個區域返回。如果沒有找到盜寶人則需要在無量走一圈.并且主id從大米那里知道盜寶人在峭壁那里之后,可以不用走第2個區域,從ct直奔峭壁殺盜寶人然后返回ct。整個遍歷效果相當令人非常滿意。

    路徑分割的原則

    原則一:去和回的路徑盡量簡單,盡量都只包含最短路線。因為這2條路徑在整個遍歷過程中都是必不可少的,無論怎么劃分,也無論盜寶人在哪里,總必須得有去有回。這種必不可少的路徑,盡量簡單能提高遍歷效率。

    原則二:有busy的小區域,花費時間較多的小區域靠后。因為遍歷的順序是從小號開始,一旦找到了盜寶人可以直接跳到最后一條路徑返回,從而避免遍歷耗時較多的區域。

    原則三:每個小區域的終點和后面任意一個小區域的起點相同。這點很重要,如此,可以從當前小區域的終點跳至后面任意一條小區域。這也是保證省略部分小區域和路徑連續遍歷的前提。

    原則四:所有小型區域加起來確實遍歷了整個大型區域的所有房間。

    原則五:在保證上面4條原則的前提下,如果你能做到所有小區域組合起來無重復或者少重復,每個小區域大小基本一致,那就更完美了。

    路徑的使用(以神龍為例)

    shenlong下面分為shenlong1-shenlong5五個小區域。@shenlong的值預先設置為5并且固定,表示shenlong一共有5個區域。

    首先制作trigger,抓取盜寶人所在地址@dbraddress(比如神龍島),根據@dbraddress的值,令@address的值為大型區域的名字(比如shenlong),這個應該很容易做到吧,2個list類型變量將中文名字和英文名字對應起來轉化就可以了。

    每個小區域路徑保存在@shenlong1-@shenlong5中,對單獨的小區域比如shenlong1,遍歷方法跟前面幾章介紹的一樣,快速行走嵌套alias為bianli

    另外制作alias bianlishenlong1,在遍歷之前對步數@step,有busy的方向@busy,需要等待的時間@time,@wait等等一系列變量的初始化并且開始執行alias bianli,這里bianli為嵌套alias,嵌套結束之后執行一個alias next,再次大概給出bianli和bianlishenlong1的定義,并且next定義如下:

    #alias bianli {#if (...) {...;%item(@path,@step);bianli} {next}}
    //滿足嵌套條件則依次取出@path中每個方向執行,并且嵌套一個bianli,嵌套條件結束執行next

    #alias bianlishenlong1 {#var path @shenlong1;#var step ...;#var steps %numitems(@path);#var busy ...;#var time ...;#var wait ...;bianli}
    //初始化各個變量,并且開始執行嵌套alias bianli

    #alias next {tell @zhanghao @pathnumber完畢,下一個[@pathnumber+1]}
    //每條小區域路徑走完,都會tell自己一句話,內容中包括了當前小區域編號和下一個小區域的編號

    #trigger {*~(@zhanghao~)告訴你:%d完畢,下一個(%d)} {#if (%1>[@@address]) {tell @zhanghao 整個路徑遍歷結束} {#if (@finddbr=0) {#var pathnumber %1;#var pathname %concat(@address,"%1");#exe bianli[@pathname]} {#var pathnumber [@@address];#var pathname %concat(@address,@pathnumber);#exe bianli[@pathname]}}} {finddbr}
    //這2個trigger要結合起來看,從表面上看非常復雜,我詳細解釋下
    //[@@address]用到了2個@符號,@address的值為shenlong,[@@address]的值就為5,注意這里必須加上[ ].如果不加[ ],@@address的值為字符串@shenlong,而不是數字5.
    //遍歷shenlong1之前,令pathnumber為1,那么依次遍歷各個小區域結束之后就會得到自己tell自己的信息,比如"1完畢,下一個2"或者"3完畢下一個4"

    //根據這句話來判斷下一個要遍歷的小區域,首先判斷%1>[@@address],如果為真,則tell @zhanghao 整個路徑遍歷結束,否則遍歷下一個區域
    //遍歷下一個區域的整個語句為#if (@finddbr=0) {#var pathnumber %1;#var pathname %concat(@address,"%1");#exe bianli[@pathname]} {#var pathnumber [@@address];#var pathname %concat(@address,@pathnumber);#exe bianli[@pathname]},第一個括號里的語句表示順序遍歷接下來的一個小區域,第二個括號里的語句表示跳至最后一個小區域返回。

    其中第一個括號里的語句為#var pathnumber %1;#var pathname %concat(@address,"%1");#exe bianli[@pathname]
    @pathnumber的值更改為%1,要遍歷的小區域的名字保存在@pathname中,#exe bianli[@pathname]開始遍歷。
    這里#var pathname %concat(@address,"%1")#exe bianli[@pathname]大家可能都感到陌生
    %concat作用為將2個字符串連接起來,舉例來說,如果%1為3,@address為shenlong,那么@pathname為shenlong3
    #exe bianli[@pathname]的作用等同于執行alias bianlishenlong3,前面講過,bianlishenlong3為對小區域shenlong3的遍歷,其中包括對@step,@busy,@wait,@time等一系列變量的初始化并且執行嵌套alias bianli

    如果第一個括號的語句都看懂了,那么第2個括號里的內容就不難理解,表示直接跳至最后一個區域比如shenlong5,以此來返回。

    至于到底是順序遍歷下一個區域還是直接跳至最后一個區域,由變量@finddbr來控制,因此你需要在遍歷之前在trigger中加入#var finddbr 0,找到盜寶人的時候#var finddbr 1.

    再來整理下神龍島區域都用到了哪些變量和alias,來更好的理解上面的遍歷方法

    @dbraddress值為神龍島,@address值為shenlong,@shenlong值為5
    @pathnumber表示當前正在遍歷的路徑編號,值只可能是1-5.@pathname表示小區域的路徑名字,值只可能是shenlong1-shenlong5
    這些變量中除了@shenlong是神龍島獨有的,另外的變量都是公用變量。簡單點,比方說慕容區域獨有的變量為@murong,至于@dbraddress,@address,@pathnumber,@pathname則屬于公用變量,對整個慕容區域的遍歷也是用這4個變量

    用到的alias有next,bianlishenlong1,bianlishenlong2,...,bianlishenlong5,以及bianli
    其中next,bianli都屬于公用alias,慕容區域的遍歷也是用這2個alias
    bianlishenlong1-5為神龍島獨有的,慕容區域的遍歷用到的alias為bianlimurong1-5

    #alias lianxubianli {#var pathnumber 1;#var pathname %concat(@address,@pathnumber);#exe bianli[@pathname]}
    //從第一條小區域路徑開始,遍歷整個大型區域@address

    至此,如果@address值為shenlong,命令行輸入lianxubianli,則按順序依次遍歷神龍島每個小區域,找到盜寶人之后繼續遍歷完當前小區域,直接跳至最后一個小區域返回。同樣如果@address的值為慕容,也是在命令行輸入lianxubianli

    注四:可以看到這個遍歷方法非常非常強大,僅僅通過胡一刀告訴你的地址,獲得地址的英文名字,然后輸入lianxubianli就可以了,而且遍歷的速度非常快。不需要使用第十三章提到的簡化和逆轉,因此也省去了很多是否可逆的判斷,而這個判斷通常非常復雜。而且當路徑不可逆時,不需要老老實實遍歷完整個路徑,速度快很多;當路徑可逆時,也僅僅只需要多走完當前沒有走完的小區域,這個時間通常不到3秒。這個方法本人大力推薦

    #trigger {盜 寶 人*~(@dbr~)} {#t- find;#var dbrnum @pathnumber;#var dbrnumber @n;#var finddbr 1} {find}
    //搜索盜寶人的trigger,執行一次關閉,@dbrnum用來保存盜寶人所在小區域的區域編號,@dbrnumber用來保存盜寶人所在房間的房間編號,@finddbr用來指示是否已經找到盜寶人
    //對于@n的值,需要在遍歷過程中數房間數目,以此來確定盜寶人在當前小區域路徑中的位置

    #trigger {*~(@zhanghao~)告訴你:%d完畢,下一個(%d)} {#if (%1>[@@address]) {tell @zhanghao 整個路徑遍歷結束} {#if (%1<=@dbrnum) {#var pathnumber @dbrnum} {#var pathnumber [@@address]}};#var pathname %concat(@address,@pathnumber);#exe bianli[@pathname]} {killdbr}
    //前面那個trigger設置為finddbr class,這個trigger設置為killdbr class,目的很明顯,就是走到盜寶人那里停下,然后殺掉,再返回。
    //每次選擇小區域時判斷%1的值,如果%1>[@@address]表示整個路徑遍歷結束;如果%1<=@dbrnum,直接跳至第@dbrnum個小區域,如果%1>@dbrnum,直接跳至最后一個小區域返回,因此走到盜寶人那里和從盜寶人那里返回都是用這個trigger

    注五:這里用到的遍歷alias還是諸如bianlishenlong1,bianlishenlong2之類的alias,用這個alias原來的定義方法來遍歷盜寶人所在小區域的時候,是停不下來的因此需要修改這個alias.這個修改不難,在bianlishenlong1中判斷(@pathnumber=@dbrnum),如果為真則@steps的值設置為@dbrnumber就可以了,如果不真,則@steps的值設置為%numitems(@shenlong1).

    注六:修改后的bianlishenlong1同樣可以用于搜索盜寶人,因為搜索前肯定將@dbrnumber,@dbrnum都初始化為0,因此搜索盜寶人的過程中@pathnumber肯定不會等于@dbrnum,可以用這個修改后的bianlishenlong1代替上面提到的bianlishenlong1,前后統一起來,沒必要另外定義新的alias,減少機器人制作工作量

    注七:至此,只要獲知盜寶人的中文地址,執行#t+ finddbr;#t+ find;#var dbrnumber 0;#var dbrnum 0;lianxubianli就可以按小區域順序搜索盜寶人了,搜索到盜寶人之后直接遍歷最后一個小區域返回,然后執行#t+ killdbr;lianxubianli就可以只選擇第1個,盜寶人所在那一個以及最后1個小區域來殺掉盜寶人以及返回。前面說了,第1個和最后1個小區域都很短,一般為最短路徑,整個過程速度非常快。

    14.2 解決亂入的2種方法

    抓取房間出口方向

    比如"east、north、west 和 south",整個字符串作為一個房間的出口信息,如果能將這個字符串轉化為list類型變量,那么對房間的出口進行操作就變的很簡單。

    房間的出口信息中一般包含:出口方向、"、"、"和"、"。"以及空格

    #tri {這里*的出口是(*)$} {#var exit "%1";#var exit %replace(@exit,"、","|");#var exit %replace(@exit,"和","|");#var exit %replace(@exit,"。","|。");#while (%item(@exit,1)!="。") {#var exit %additem(%trim(%item(@exit,1)),@exit);#delnitem exit 1};#delnitem exit 1} {exit}
    //思路很簡單,將"、"以及"和"都替換成"|",將空格去掉,最后去掉"。"
    //比如房間出口是east、north、west 和 south。那么#var exit %replace(@exit,"、","|");#var exit %replace(@exit,"和","|");#var exit %replace(@exit,"。","|。")這3個命令的結果是@exit的值為" east|north|west | south|。"
    //#while命令,判斷@exit的第一個item,如果不是句號,就取出第一個item消除空格之后添加到@exit的末尾,然后刪除第一個item。簡單點說就是把第一個item消除空格之后移動到最后面去,一直循環判斷知道第一個item為句號。實際上整個#while語句的作用就是消除空格
    //最后刪除句號就可以了


    #tri {這里沒有任何明顯的出路。} {#var exit {}} {exit}
    //沒有出口,@exit賦值為空


    有了這2個trigger,房間的出口信息都保存在@exit中,@exit為list變量,可以輕易知道該房間的任意一個方向以及房間出口方向的數目

    解決亂入方法一

    通過大米跟隨護鏢,亂入之后look各個方向,看到大米后選擇正確的方向回去。這個方法只能解決1步亂入,護鏢任務改動之后這個方法已經沒什么用了,僅僅靠這個是無法全自動的,我簡單說下。

    #tri {劫匪趁你不注意,推著鏢車就跑,你趕緊追了上去。} {#t+ exit;tell @zhanghao 亂入}
    //鏢車移位置,開啟抓取房間出口信息的trigger


    #tri {@myname~(@zhanghao~)告訴你:亂入} {#t-exit;#t+ dummy;#var dummynum 0;#forall @exit {look %i}}
    //房間出口信息抓取完畢,關閉exit class,然后打開觀察大米的trigger,并且look房間的所有出口

    #tri {^(*)%s-%s$} {#add dummynum 1} {dummy}
    //對房間名計數

    #tri {*@dummyname~(*~)$} {#t- dummy;gan che to %item(@exit,@dummynum)} {dummy}
    //注意exit class在觸發之后關閉了,所以look各個方向的時候不會更新@exit,因此@exit的值為當前房間的出口信息
    //看到大米了,關閉dummy class,把鏢車趕過去.大米的名字預先保存在變量@dummyname中

    解決亂入方法二

    其實是方法一的推廣,方法一只是這個方法的一個特例,此方法可回答本文開頭提出的第3個問題

    實例:3已知你的大米或者npc在你附近不超過n步的某個房間,通過抓取房間出口方向來遍歷n步以內所有房間,并且在找到大米或者npc時能夠停下來

    通過房間的出口信息,遍歷當前位置任意預先指定步數以內的所有房間并且返回。遍歷結束后,能得到從當前位置到大米所在房間的路徑,沿著路徑把鏢車趕過去就可以了。下面給出遍歷并且得到路徑的代碼

    普遍采用的遍歷方法有2種,即廣度優先和深度優先。這2種方法都可以實現,下面采用廣度優先寫出遍歷方法。從理論上來說廣度優先要快,但是當我在mud中實際試驗2兩種方法,比較之下發現,當亂入步數較多時,廣度優先走了很多重復路,遍歷速度不如深度優先,而當亂入步數較少時,由于遍歷時間較短,廣度優先體現不出優勢,所以實際上深度優先更好一些。對于深度優先方法有興趣可以自己嘗試寫一下,深度優先要簡單一些。

    小知識:廣度優先的順序是,1,2,...,max,(度數加1),1-1,1-2,1-3,...,1-max,2-1,2-2,...,2-max,3-1,...,3-max,,,,,max-max,(度數+1),1-1-1,1-1-2,...,1-1-max,1-2-1,1-2-2,...
    度數是廣度優先遍歷的一個專業術語,在這里度數表示到達某個房間需要走的步數
    比如某個房間需要先走第a個方向,再走第b個方向,再走第c個方向,最后再走第d個方向才能到達,那么該房間的度數為4,用a-b-c-d來表示房間的編號

    可以看到,只有當全部為max的時候,度數+1。采用廣度優先遍歷,度數小的房間將被優先遍歷到。

    實現思路:

    先遍歷所有的1度房間,然后遍歷所有的2度房間一直進行下去

    舉例來模擬一下遍歷過程,以便更好的看懂下面給出的代碼。

    正在遍歷4度房間,如果房間的編號為a-b-c-d,那么先退回一步成為a-b-c,然后判斷d是否為最后一個出口,如果是則繼續退回一步,判斷c;否則前進一步,進入a-b-c-(d+1)號房間
    如果連續退了2步,房間編號為a-b,判斷c不為max,此時按上面的判斷需要前進一步,到達a-b-(c+1)號房間.這時房間度數為3,不足4度,用1補齊,進入a-b-(c+1)-1號房間
    上面的過程將一直持續到到達編號為max-max-max-max的房間,此時繼續上面的過程將連續退4步,房間編號為空,表示所有4度房間都遍歷完畢,此時度數+1,開始遍歷所有5度房間。

    具體實現方法:

    #tri {這里*的出口是(*)$} {#var exit "%1";#var exit %replace(@exit,"、","|");#var exit %replace(@exit,"和","|");#var exit %replace(@exit,"。","|。");#while (%item(@exit,1)!="。") {#var exit %additem(%trim(%item(@exit,1)),@exit);#delnitem exit 1};#delnitem exit 1} {exit}

    #tri {這里沒有任何明顯的出路。} {#var exit {}} {exit}
    //跟上面一樣,僅僅抓取房間出口信息

    #tri {劫匪趁你不注意,推著鏢車就跑,你趕緊追了上去。} {#t+ exit;判斷劫匪是否被打跑?}
    //開始抓取房間出口信息的trigger,開啟判斷劫匪是否已經被打跑的方法,此方法自己補充完整


    #tri <劫匪已經被打跑> {#var dummynum 0;#var degree 0;#var searchpath {};#var pathnum {};tell @zhanghao 開始遍歷}
    //劫匪已經被打跑,可以開始遍歷了,先初始化遍歷需要用到的變量
    //@degree設置成0度,表示開始遍歷0度房間,即當前房間
    //@searchpath表示從鏢車所在位置到當前所在位置的路徑,當前位置和鏢車所在位置相同,所以賦值為空.
    //@pathnum表示當前房間的編號,編號為a-b-c-d的房間賦值為a|b|c|d。當前房間為0度房間,所以賦值為空.

    #tri {@myname~(@zhanghao~)告訴你:開始遍歷} {#if (%numitems(@pathnum)<@degree) {#var searchpath %additem(%item(@exit,1),@searchpath);#var pathnum %additem(1,@pathnum);%item(@exit,1);tell @zhanghao 開始遍歷} {#t- exit;#var roomnum 0;#var dummynum 0;#t+ dummy;#forall @exit {look %i};tell @zhanghao 是否找到大米}}
    //房間度數不足,用1補齊,否則關閉exit class,仍然采用look當前房間所有出口來尋找大米,@roomnum用于房間名計數,@dummynum用來保存大米所在的房間名計數,look之前這2個變量賦值為0


    #tri {^(*)%s-%s$} {#add roomnum 1} {dummy}
    //對房間名計數


    #tri {*@dummyname~(*~)$} {#var dummynum @roomnum} {dummy}
    //如果找到大米,得到@dummynum的值

    #tri {@myname~(@zhanghao~)告訴你:是否找到大米?} {#t- dummy;#if @dummynum {tell @zhanghao 成功找到大米} {tell @zhanghao 沒有找到大米}}
    //先關閉dummy class,如果@dummynum的值不為0,則表示找到大米,否則表示沒有找到大米

    #tri {@myname~(@zhanghao~)告訴你:成功找到大米} {#var searchpath %additem(%item(@exit,@dummynum),@searchpath);tell @zhanghao 路徑已經得到,沿著路徑趕回去}
    //將第@dummynum個方向添加到@searchpath中,得到從鏢車所在位置到當前房間再到大米所在位置的路徑,其實就是從鏢車所在位置到大米所在位置的路徑,接下來要做的就是沿著@searchpath返回鏢車那里,然后沿著@searchpath把鏢車推到大米那里亂入就解決了

    //如果沿著@searchpath推車的時候再次亂入也沒有關系,大米沒有改變過位置,只需要重新解決亂入即可

    #tri {@myname~(@zhanghao~)告訴你:沒有找到大米} {#t+ exit;#if (%numitems(@pathnum)=0) {#add degree 1;tell @zhanghao 開始遍歷} {goback}}
    //沒有找到大米,開啟exit class,如果當前房間度數為0,則@degree加1,開始遍歷所有1度房間;否則退回1步,用alias goback返回


    #var fangxiang {north|east|northwest|northeast|northup|northdown|eastup|eastdown|up|enter|south|west|southeast|southwest|southdown|southup|westdown|westup|down|out}

    #var fangxiangb {south|west|southeast|southwest|southdown|southup|westdown|westup|down|out|north|east|northwest|northeast|northup|northdown|eastup|eastdown|up|enter}

    #alias goback {#var temp %item(@pathnum,%numitems(@pathnum));#var temp2 %item(@searchpath,%numitems(@searchpath));#delnitem pathnum %numitems(@pathnum);#delnitem searchpath %numitems(@searchpath);%item(@fangxiangb,%ismember(@temp2,@fangxiang));tell @zhanghao 已經退回一步}
    //將@pathnum,@searchpath的最后一個item分別保存在臨時變量@temp和@temp2中,然后從@pathnum和@searchpath中刪除最后一個item,并且往回走一步,用"tell @zhanghao 已經退回一步"作為接下來的觸發


    #tri {@myname~(@zhanghao~)告訴你:已經退回一步} {#if (%numitems(@pathnum)=0|&@temp<%numitems(@exit)) {bianlinextdegree} {#if (@temp<%numitems(@exit)) {#add temp 1;#var temp2 %item(@exit,@temp);#var pathnum %additem(@temp,@pathnum);#var searchpath %additem(@temp2,@searchpath);@temp2;tell @zhanghao 開始遍歷} {goback}}}
    //第一個#if判斷是否退到頭了,如果是則度數加1,開始遍歷更高度數的所有房間;否則進入第二個#if判斷
    //第二個#if判斷剛剛后退的那一步是否為房間的最后一個出口,如果不是則進入下一個出口繼續遍歷;否則再退一步
    //此代碼完全按照前面給出的遍歷思路來寫


    #alias bianlinextdegree {#if (@degree<@degrees) {#add degree 1;tell @zhanghao 開始遍歷} {tell @zhanghao 遍歷結束,亂入解決失敗;quit}}
    //如果@degree<@degrees,度數+1,開始遍歷更高度數的所有房間;否則遍歷結束,亂入解決失敗,退出游戲,護鏢失敗
    //@degrees用來控制遍歷范圍,比如事先設定@degrees為4,則將遍歷所有4度以內的房間


    代碼結束

    注七:此方法仍然采用look房間出口的作法來尋找大米,因此如果@degrees為4,實際5步以內的所有房間都將被搜索到

    注八:此方法已通過實際檢驗,zmud555的同學可將代碼復制到mud中進行試驗。我將大米放在襄陽城的帥府大門,然后從帥府大門走n;e;e;e;e,命令行輸入#t- dummy;#t+ exit;#var degrees 9;#var dummynum 0;#var degree 0;#var searchpath {};#var pathnum {};look;tell [@zhanghao] 開始遍歷,遍歷就開始了。最后得出@searchpath的值為east|west|west|west|west|west|south,度數@degrees設置為9,搜索到6度房間時搜索停止。可以看到@searchpath的前2個item互相可逆,這是由于@exit中包含不僅包含去后面房間的出口,還包含返回前面房間的出口,可以對@searchpath使用路徑的簡化,也可以對上面的方法進行修改,只需要修改抓取房間出口信息的trigger就可以了,修改如下:

    #tri {這里*的出口是(*)$} {#var exit "%1";#var exit %replace(@exit,"、","|");#var exit %replace(@exit,"和","|");#var exit %replace(@exit,"。","|。");#while (%item(@exit,1)!="。") {#var exit %additem(%trim(%item(@exit,1)),@exit);#delnitem exit 1};#delnitem exit 1;#var temp3 %item(@searchpath,%numitems(@searchpath));#var temp3 %ismember(@temp3,@fangxiang);#var temp3 %item(@fangxiangb,@temp3);#delitem exit @temp3} {exit}
    //
    抓取出口信息的時候刪除掉返回前面房間的出口
    修改后再試驗上面的例子,得到@searchpath的值為west|west|west|west|south

    注九:當通過房間出口方向進入下一個房間有阻礙時,比如店小二要求交錢才允許up,衙門不讓進,此時遍歷搜索會出問題,上面的方法需要再做修改,這個不難。修改如下:

    #tri {這里*的出口是(*)$} {#var exit "%1";#var exit %replace(@exit,"、","|");#var exit %replace(@exit,"和","|");#var exit %replace(@exit,"。","|。");#while (%item(@exit,1)!="。") {#var exit %additem(%trim(%item(@exit,1)),@exit);#delnitem exit 1};#delnitem exit 1;#var temp3 %item(@searchpath,%numitems(@searchpath));#var temp3 %ismember(@temp3,@fangxiang);#var temp3 %item(@fangxiangb,@temp3);#delitem exit @temp3;#if (@where=客店|@where=中央廣場) {#delitem exit up};#if (@where=帥府大門) {#delitem exit south}} {exit}
    //這里,通過房間的名稱,將@exit中不能走的方向刪除,比如客店和中央廣場刪除up方向,帥府大門刪除south方向,這樣遍歷搜索時這些不能行走的方向相當于屏蔽掉了。當亂入到客店二樓或者賞月臺時候,先通過房間名稱將鏢車往down方向趕一步,再開始搜索就可以了
    //@where的值通過下面的trigger獲取

    #tri {^(*)%s-%s$} {#var where "%1";#add roomnum 1}

    注十:至于深度優先遍歷,實現起來比這個要簡單,遍歷度數較小時,實際上2種方法在速度上差別不大,有興趣的同學可以自己嘗試寫寫深度優先遍歷

    注十一:當亂入步數小于3時,無論是廣度優先還是深度優先,速度都很快,特別當僅亂入1步時,搜索在1秒內完成。亂入步數再多一些的話,遍歷花費時間就比較長了,因此建議搜索時@degrees設置為2,當找不到大米時采用上一章介紹的辦法。事實上不用這個方法,只用上一章介紹的方法也完全可以。

    注十二:這個方法不僅可以用來解決亂入,也可以用來小區域搜索店鋪伙計。我在自己的機器人中沒有采用這個方法,這個方法只是為了本章專門寫出來的,此方法改進的余地應該還很大。

    高級篇總結:至此,高級篇圍繞list變量遍歷,已經介紹了多種遍歷方法,其中胡一刀和護鏢機器人的遍歷方法都不止介紹了一種。事實上不僅僅只有文中介紹到的方法,并且文中介紹到的方法也肯定不會是最佳方法,相信肯動腦筋的同學一定能創造出更好的方法。在寫整篇文章包括本章的過程中,重新對zmud的各種方法技巧回顧歸納整理,并且從大家的回帖中,都學到了不少東西。

    posted on 2011-08-17 17:16 常言笑 閱讀(10629) 評論(0)  編輯  收藏


    只有注冊用戶登錄后才能發表評論。


    網站導航:
     

    My Links

    Blog Stats

    常用鏈接

    留言簿(5)

    隨筆分類

    隨筆檔案

    搜索

    積分與排名

    最新評論

    閱讀排行榜

    評論排行榜

    主站蜘蛛池模板: 久久精品女人天堂AV免费观看| 亚洲午夜无码久久| 亚洲AV无码成人网站久久精品大| 国产AV无码专区亚洲AV毛网站| 亚洲人成网www| 亚洲成人激情小说| 黄床大片免费30分钟国产精品| 久久国产精品免费视频| 噼里啪啦电影在线观看免费高清| 亚洲理论精品午夜电影| 男人j进女人p免费视频| 久久久免费的精品| 成人永久免费福利视频网站| 国产亚洲精品资源在线26u| 国产AV旡码专区亚洲AV苍井空| 免费无码肉片在线观看| 亚洲人成色在线观看| 久久精品无码精品免费专区| 亚洲福利一区二区精品秒拍| 成年私人影院免费视频网站| 亚洲av无码专区在线播放| 免费国产污网站在线观看15| 免费又黄又爽又猛的毛片| 亚洲国产美女福利直播秀一区二区| caoporm碰最新免费公开视频| 日韩吃奶摸下AA片免费观看| 白白色免费在线视频| 免费精品人在线二线三线区别 | 久久丫精品国产亚洲av不卡| 四虎精品成人免费视频| 在线免费观看色片| 亚洲人成77777在线观看网| 精品久久久久久久免费加勒比| 亚洲精品福利网站| 国产美女无遮挡免费视频| 亚洲真人无码永久在线观看| 亚洲男人天堂2020| 男女啪啪免费体验区| 水蜜桃亚洲一二三四在线| 四虎影视无码永久免费| 亚洲国产成人影院播放|