謝作如 林淼焱
很多人都知道,Chrome瀏覽器在出現(xiàn)無網(wǎng)絡(luò)連接時會提供一個恐龍小游戲,幫我們緩解一下情緒。只要按下空格鍵,這只小恐龍就會動起來,開始在沙漠中狂奔,然后會有仙人掌、飛鳥等源源不斷地迎面而來,玩家需要操控小恐龍?zhí)S到空中,以躲避這些障礙物(如圖1)。在瀏覽器地址欄輸入chrome://dino/,聯(lián)網(wǎng)狀態(tài)下也可以玩這個游戲。
因?yàn)槭芫W(wǎng)絡(luò)控制,學(xué)生在機(jī)房會經(jīng)??吹竭@個頁面,也會經(jīng)常玩這個游戲。這給我們一個靈感:為什么不讓學(xué)生試試用Python代碼編寫一個自動玩游戲的腳本,然后比賽誰寫的代碼效果好呢?這可是一個有挑戰(zhàn)性的任務(wù)。
● 解決方案分析
能不能用Python腳本自動“玩”這個瀏覽器游戲?答案不言而喻,借助Python強(qiáng)大的第三方庫,不僅可以對障礙物進(jìn)行識別,還能模擬鍵盤輸入,讓小恐龍?zhí)饋?,達(dá)到我們想要的自動玩游戲的效果。
用流程圖的形式來描述玩小恐龍游戲的過程,只有兩個核心的工作流程,即判斷前方是否有障礙和按下空格躲避障礙,用流程圖來描述,如圖下頁2所示。
接下來,分析這個游戲的玩法,要想實(shí)現(xiàn)自動“玩”,我們需要解決三個問題:①如何獲得當(dāng)前的障礙物信息?②如何模擬按鍵讓小恐龍?zhí)饋恚竣廴绾卧谶m當(dāng)?shù)臅r間跳起來?
問題②是最容易解決的。Python有很多庫可以模擬鍵盤,如在Windows系統(tǒng)下,常用的是調(diào)用win32api庫,非Windows系統(tǒng)的計(jì)算機(jī)可以用pykeyboard。但是考慮到判斷障礙物需要用到截圖,而截圖方面比較好用的庫是pyautogui,因此選擇用pyautogui的typewrite方法。代碼如下:
pyautogui.typewrite(message='? ')
引號中的字符就是要模擬的按鍵,如果是空格,那么就在引號中放一個空格。也可以用“pyautogui.press("space")”的形式,“space”表示空格鍵。
所以,這個工作流程中的核心算法,是問題①和③,即判斷障礙物信息,并在合適時間跳起來。
● 核心算法的解決
觀察游戲界面可以知道,如果小恐龍“眼前”的區(qū)域都是白色,那么說明沒有障礙物,如果出現(xiàn)了其他顏色,說明障礙物來了,如圖3中框選區(qū)域所示。要想讓計(jì)算機(jī)自動判斷是否有障礙物,可以用定時截圖的方式,然后不斷“識別”框中區(qū)域的顏色,再根據(jù)識別結(jié)果來確定是否按下空格鍵。具體實(shí)現(xiàn)方法如下。
1.截圖并且識別障礙物
通過pyautogui庫完成屏幕截圖后,再借助PIL庫,來識別目標(biāo)區(qū)域的顏色。為了加快判斷的速度,我們可以繼續(xù)裁剪圖像,裁去場景中的云朵、地面等干擾物,僅剩下框中的內(nèi)容。
識別圖片的顏色,大家想到的方法可能是讀取圖片中每個像素點(diǎn)的數(shù)據(jù),將其相加并求出平均數(shù),如果這個數(shù)字小于255,說明圖片不是純白色,包含黑色的障礙物。其實(shí)還有一種更簡單的方法,即利用PIL庫中的getcolors方法,統(tǒng)計(jì)整張圖片中所有像素點(diǎn)的顏色及其數(shù)量。
對于Python來說,要想提高執(zhí)行代碼的效率,應(yīng)該盡可能調(diào)用庫中的方法。getcolors方法會返回一個列表,如[((顏色1像素點(diǎn)的數(shù)量),(顏色1RGB值))]。圖4中的案例結(jié)果就代表圖片中共有68751個白色像素點(diǎn)、3011個綠色像素點(diǎn)、1958個紅色像素點(diǎn)、2522個藍(lán)色像素點(diǎn)。
有了這個方法,判斷顏色就非常簡單了。用getcolors方法返回顏色的列表,再用len()函數(shù)就能得到圖片中所有顏色的種類數(shù)。沒有障礙物時,顏色是1,即只有白色,大于1就說明有障礙物了。參考代碼如下頁圖5所示。
2.選擇適當(dāng)?shù)奶S時機(jī)
當(dāng)小恐龍“眼前”不止一種顏色時,說明障礙物已經(jīng)接近,需要讓小恐龍及時跳躍,進(jìn)行躲避。
一般來說,用下頁圖6這樣的語句就能讓小恐龍?zhí)^很多障礙物,并且得到很高的分?jǐn)?shù)了。但是,當(dāng)速度越來越快的時候,是不是還要繼續(xù)判斷列表中的白色數(shù)量?因?yàn)榘咨臄?shù)量越小,說明障礙物已經(jīng)完全進(jìn)入了區(qū)域?;蛘呤欠褚鄤澐謳讉€區(qū)域,然后得到障礙物離小恐龍的位置?這就要不斷調(diào)試,找到最適合的時機(jī)。
● 代碼實(shí)現(xiàn)和測試
將上述代碼合并在一起,添加循環(huán),使得程序能夠不斷進(jìn)行判斷,這個能夠自動控制小恐龍的“外掛”代碼就寫好了。參考代碼如下頁圖7所示。
因?yàn)椴煌挠?jì)算機(jī)顯示屏的大小也是不一樣的,我們要根據(jù)分辨率微調(diào)截圖參數(shù),以達(dá)到最好的效果。為了更好地確定位置,可以用PIL庫的ImageDraw模塊來畫出區(qū)域,代碼如圖8所示。
經(jīng)過測試,我們這段簡單的代碼居然可以拿到高于1000的分?jǐn)?shù)。這是我們平時根本沒辦法達(dá)到的分?jǐn)?shù)。當(dāng)看到小恐龍?jiān)谄聊簧喜粩嘧詣犹S,一種特別的滿足感會油然而生。
● 算法的優(yōu)化
當(dāng)達(dá)到一定分?jǐn)?shù)后,這個小游戲的場景會在黑夜與白天之間切換,且障礙物移動速度增加,大大增加了游戲的難度。上述代碼最高能夠到達(dá)1500分左右,由于障礙物的提速,會讓小恐龍?jiān)诼涞厍熬妥采舷乱粋€障礙物,想要繼續(xù)提高分?jǐn)?shù),就需要繼續(xù)優(yōu)化代碼。比如,①讓小恐龍看得更遠(yuǎn):隨著分?jǐn)?shù)的增加,不斷擴(kuò)大截取圖片的范圍,直到這個范圍增加到屏幕右端為止。②提前預(yù)判起跳時間:在圖片的中間和右端各截取一張圖片,根據(jù)障礙物的通過時間,計(jì)算其速度,列出算式,求得障礙物到達(dá)小恐龍前方的時間,小恐龍?jiān)诘却鄳?yīng)的延時后,精準(zhǔn)起跳。Pyautogui庫中還有keyDown和keyUp的控制鍵盤方法,可以實(shí)現(xiàn)短按和長按的效果。
● 總結(jié)
《普通高中信息技術(shù)課程標(biāo)準(zhǔn)(2017年版)》在必修模塊1中提到,要讓學(xué)生“通過解決實(shí)際問題,體驗(yàn)程序設(shè)計(jì)的基本流程,感受算法效率,掌握程序調(diào)試與運(yùn)行的方法”,而控制游戲這種有趣的案例,既源于生活又具有不斷優(yōu)化的特點(diǎn),寓教于樂,很適合作為教學(xué)的拓展案例,讓學(xué)生在探索過程中提高編程能力。此外,這種“自動化”的思想,能夠引發(fā)學(xué)生對人工智能的學(xué)習(xí)興趣,值得在教學(xué)中推廣。