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