Python微信跳一跳輔助原理解析

2021-02-13 Hackerchai的技術窩


微信跳一跳

2017 年 12 月 28 日下午,微信發布了 6.6.1 版本,加入了「小遊戲」功能,並提供了官方 DEMO「跳一跳」。這是一個 2.5D 插畫風格的益智遊戲,玩家可以通過按壓屏幕時間的長短來控制這個「小人」跳躍的距離。分數越高,那麼在好友排行榜更加靠前。

輔助腳本

wechat_jump_game(https://github.com/wangshub/wechat_jump_game)是一個python2編寫的輔助外掛。操作簡單,易於配置,支持Windows和Mac平臺,通過移植也可以跑在Linux平臺。輔助通過Python的圖片操作庫分析手機實時的截圖,通過圖像識別獲取棋子位置和棋盤的位置,從而通過ADB模擬點擊操作完成自動操作。

下面是輔助腳本工作的視頻:

輔助工作原理

adb shell screencap -p /sdcard/autojump.png
adb pull /sdcard/autojump.png .

adb shell input swipe x y x y time(ms)

思路以及代碼分析

核心:我們這次講解以最常用最好用的自動腳本為例。每次落穩之後截圖,根據截圖算出棋子的坐標和下一個塊頂面的中點坐標,根據兩個點的距離乘以一個時間係數獲得長按的時間

1.首先腳本會檢測ADB的可用性,並獲取當前手機型號,解析度等信息。

def _get_screen_size():
   '''
   獲取手機屏幕大小
   '''
   size_str = os.popen('adb shell wm size').read()
   if not size_str:
       print('請安裝 ADB 及驅動並配置環境變量')
       sys.exit()
   m = re.search(r'(\d+)x(\d+)', size_str)
   if m:
       return "{height}x{width}".format(height=m.group(2), width=m.group(1))
   return "1920x1080"

注意re.serarch是正則表達式操作,目的是從ADB輸出中提取出屏幕解析度的兩個數字。

def open_accordant_config():
   '''
   調用配置文件
   '''
   screen_size = _get_screen_size()
   config_file = "{path}/config/{screen_size}/config.json".format(
       path=sys.path[0],
       screen_size=screen_size
   )
   if os.path.exists(config_file):
       with open(config_file, 'r') as f:
           print("Load config file from {}".format(config_file))
           return json.load(f)
   else:
       with open('{}/config/default.json'.format(sys.path[0]), 'r') as f:
           print("Load default config")
           return json.load(f)

這段函數實際的操作是通過獲取的屏幕大小獲取之前已經調試好的數據,包括根據屏幕大小測算好的二分之一的棋子底座高度,棋子的寬度,長按的時間係數(後文會繼續說明)等等。

2.截圖以及從手機拉取到PC

if screenshot_way == 2 or screenshot_way == 1:
       process = subprocess.Popen('adb shell screencap -p', shell=True, stdout=subprocess.PIPE)
       screenshot = process.stdout.read()
       if screenshot_way == 2:
           binary_screenshot = screenshot.replace(b'\r\n', b'\n')
       else:
           binary_screenshot = screenshot.replace(b'\r\r\n', b'\n')
       f = open('autojump.png', 'wb')
       f.write(binary_screenshot)
       f.close()

3.關鍵步驟:尋找棋子坐標和下一跳棋盤坐標

這段代碼的核心是Python的PIL庫操作,如果不了解請自行Google學習。

def find_piece_and_board(im):
   '''
   尋找關鍵坐標
   '''
   w, h = im.size

   piece_x_sum = 0
   piece_x_c = 0
   piece_y_max = 0
   board_x = 0
   board_y = 0
   scan_x_border = int(w / 8)  
   scan_start_y = 0  
   im_pixel = im.load()
   
   for i in range(int(h / 3), int(h*2 / 3), 50):
       last_pixel = im_pixel[0, i]
       for j in range(1, w):
           pixel = im_pixel[j, i]
           
           if pixel[0] != last_pixel[0] or pixel[1] != last_pixel[1] or pixel[2] != last_pixel[2]:
               scan_start_y = i - 50
               break
       if scan_start_y:
           break
   print('scan_start_y: {}'.format(scan_start_y))

   
   for i in range(scan_start_y, int(h * 2 / 3)):
       for j in range(scan_x_border, w - scan_x_border):  
           pixel = im_pixel[j, i]
           
           if (50 < pixel[0] < 60) and (53 < pixel[1] < 63) and (95 < pixel[2] < 110):
               piece_x_sum += j
               piece_x_c += 1
               piece_y_max = max(i, piece_y_max)

   if not all((piece_x_sum, piece_x_c)):
       return 0, 0, 0, 0
   piece_x = int(piece_x_sum / piece_x_c)
   piece_y = piece_y_max - piece_base_height_1_2  

這段代碼開始搜尋棋子底端近似一條直線的特徵,通過一個近似的RGB顏色區域來判斷是否是棋子,當程序搜尋到最後一行的時候,計算這一行所都X坐標的平均值記做中點X值,然後將Y坐標向上移動一半棋子底盤高度的值,也就是定位到棋子底面中心,以此確定最終棋子的確切位置

if piece_x < w/2:
       board_x_start = piece_x
       board_x_end = w
   else:
       board_x_start = 0
       board_x_end = piece_x

上述代表根據棋子坐標確定下一跳棋盤的開始結束坐標

for i in range(int(h / 3), int(h * 2 / 3)):
       last_pixel = im_pixel[0, i]
       if board_x or board_y:
           break
       board_x_sum = 0
       board_x_c = 0

       for j in range(int(board_x_start), int(board_x_end)):
           pixel = im_pixel[j, i]
           
           if abs(j - piece_x) < piece_body_width:
               continue

           
           if abs(pixel[0] - last_pixel[0]) + abs(pixel[1] - last_pixel[1]) + abs(pixel[2] - last_pixel[2]) > 10:
               board_x_sum += j
               board_x_c += 1
       if board_x_sum:
           board_x = board_x_sum / board_x_c
   last_pixel = im_pixel[board_x, i]

   
   
   for k in range(i+274, i, -1):
       pixel = im_pixel[board_x, k]
       if abs(pixel[0] - last_pixel[0]) + abs(pixel[1] - last_pixel[1]) + abs(pixel[2] - last_pixel[2]) < 10:
           break
   board_y = int((i+k) / 2)

   
   
   for l in range(i, i+200):
       pixel = im_pixel[board_x, l]
       if abs(pixel[0] - 245) + abs(pixel[1] - 245) + abs(pixel[2] - 245) == 0:
           board_y = l+10
           break

   if not all((board_x, board_y)):
       return 0, 0, 0, 0

   return piece_x, piece_y, board_x, board_y

這時候從邊界最上方一行一行掃描,由於圓形的塊最頂上是一條線,方形的上面大概是一個點,所以就用類似識別棋子的做法多識別了幾個點求中點,這時候得到了塊中點的 X 軸坐標,這時候假設現在棋子在當前塊的中心,從上頂點往下 +274 的位置開始向上找顏色與上頂點一樣的點,為下頂點,取兩者平均則是棋盤上平面中心點Y坐標。另外,如果上一跳命中中間,則下個目標中心會出現 r245 g245 b245 的白點,尋找這個白點可以直接找到棋盤上平面的中心點Y坐標

中心點如圖:

274像素的尋找方式:

4.測算兩點距離和點按時間確定

math.sqrt((board_x - piece_x) ** 2 + (board_y - piece_y) ** 2)

def jump(distance):
   '''
   跳躍一定的距離
   '''
   press_time = distance * press_coefficient
   press_time = max(press_time, 200)  
   press_time = int(press_time)

上述代碼提及了press_coefficient也就是上文提到的長按的時間係數,這個量與屏幕大小有關,作者通過大量的調試數據為我們提供了這個比例係數,在腳本初始化時,腳本會根據屏幕解析度(個別手機需要單獨考慮)來讀取各自的config文件,獲取這個係數,下面是1080*1920解析度屏幕的config文件:

{
   "under_game_score_y": 300,
   "press_coefficient": 1.392,
   "piece_base_height_1_2": 20,
   "piece_body_width": 70,
   "swipe": {
       "x1": 500,
       "y1": 1600,
       "x2": 500,
       "y2": 1602
   }
}

從github裡這麼多解析度的適配config可以看出,這不是一個人的工作,包括一些特殊機型的適配文件,這些都是Github上眾多開發者共同協作的結果,這就是開源精神。

其本質是通過adb命令,給手機模擬按壓事件

adb shell input swipe x y x y time

其中 x 和 y 是屏幕坐標, time 是觸摸時間,單位ms。在代碼中可以參見jump()函數。以上就完成了一次完整的操作。

5.整體循環邏輯和防和諧

while True:
       pull_screenshot()
       im = Image.open('./autojump.png')
       
       piece_x, piece_y, board_x, board_y = find_piece_and_board(im)
       ts = int(time.time())
       print(ts, piece_x, piece_y, board_x, board_y)
       set_button_position(im)
       jump(math.sqrt((board_x - piece_x) ** 2 + (board_y - piece_y) ** 2))
       if debug_switch:
           debug.save_debug_screenshot(ts, im, piece_x, piece_y, board_x, board_y)
           debug.backup_screenshot(ts)
       i += 1
       if i == next_rest:
           print('已經連續打了 {} 下,休息 {}s'.format(i, next_rest_time))
           for j in range(next_rest_time):
               sys.stdout.write('\r程序將在 {}s 後繼續'.format(next_rest_time - j))
               sys.stdout.flush()
               time.sleep(1)
           print('\n繼續')
           i, next_rest, next_rest_time = 0, random.randrange(30, 100), random.randrange(10, 60)
       time.sleep(random.uniform(0.9, 1.2))  

以上是完整的主函數循環邏輯,其中還為了防止微信官方屏蔽特意加入了「喘息時間」的機制,即一定步數之後休眠一定時間,還是很機智的呀。

仍然存在的問題

由於實現的方法還存在漏洞,程序還存在以下問題:

1.該算法對所有純色平面和部分非純色平面有效,對高爾夫草坪面、木紋桌面、藥瓶和非菱形的碟機(好像是)會判斷錯誤,若上一跳由於某種原因沒有跳到正中間,而下一跳恰好有無法正確識別花紋,則有可能遊戲失敗,由於花紋面積通常比較大,失敗概率較低

2.還沒有智能識別唱片機等加分物件。

總結

此輔助是一個很具有趣味性的Python小程序,可以作為研究Python PIL編程和簡單圖片識別入門的小項目。有興趣的話還可以做一些優化給作者pull request啊~

相關焦點

  • python微信跳一跳 小白 教程
    最近微信更新6.6.1後,推出微信小遊戲,跳一跳有當年Flappy Bird的感覺。
  • Python教程——製作「微信跳一跳」輔助工具
    跳一跳是一個最近出的一個比較熱然後估計馬上就要涼了的微信小遊戲,一出來就有各路大神直接實現了各種掛。        和主流版本一樣,利用adb截取遊戲圖片,然後利用matplotlib和numpy畫出一張一樣的遊戲截圖,然後用滑鼠點一下當前棋子的位置獲取當前棋子的坐標,接著再點一下下一個要跳的平臺獲取平臺的坐標,然後計算出之間的距離,用adb模擬按壓即可。
  • 微信跳一跳遊戲輔助工具詳細的圖文操作教程-不用外掛照樣自動刷分
    這篇文章除了介紹一下微信小程序,還應不少的朋友的要求寫一下微信跳一跳遊戲自動刷分方法。    一、跳一跳輔助準備工作關於微信跳一跳遊戲輔助工具其實網上已經有了不少,只不過大佬們寫的工具使用說明過於簡單,一些從來沒有接觸過Andriod手機開發的朋友根本就「丈二和尚摸不著頭腦」,這裡我從眾多工具中測試中選取一個最為簡單的,以便為更多的朋友上手使用。
  • 微信跳一跳輔助程序
    天寫一個微信跳一跳輔助程序這個項目我是跟著一個視頻課程做的雖然我測試的時候,最後得到的分數被判定為作弊
  • 微信跳一跳分數怎麼作弊?黑松鼠跳一跳輔助外掛使用方法說明
    微信最近出現了一款叫跳一跳的小遊戲,非常好玩的哦,只不過想要拿到高分還是很有難度的,黑松鼠跳一跳可以讓你輕鬆拿到高分,那麼要怎麼使用這個黑松鼠跳一跳呢,和小編一起來看看吧~  黑松鼠跳一跳怎麼用 使用方法介紹  1、首先用戶需要打開黑松鼠跳一跳輔助,設置好遊戲參數,微調為-35,如果跳不準的話,可在-100到100之間調節;  2、
  • 「跳一跳」輔助高分攻略
    前段時間微信更新了最新版本 (6.6.1版本),沉寂了一年的微信小程序開始迎接了它的春天,其中微信官方開發的「跳一跳」就開始肆虐了我的微信社交平臺
  • 微信小程序跳一跳輔助程序指北
    一、本實驗使用的環境: Win7 64位+華為榮耀9 二、準備: 電腦與手機均安裝豌豆莢,手機調到微信小程序跳一跳首頁界面
  • 100 行代碼實現『跳一跳』輔助
    分享一下今天下午用python寫的"跳一跳"小遊戲的輔助程序。之前是準備用樹莓派操控一個"機械手指"來代替人的觸摸操作,但該方案還在醞釀中,實現了再分享。接下來要分享的是用"純軟體"的方法來玩"跳一跳"。
  • 微信跳一跳高分教學
    下面開始教程本教程的教學對象是不懂python的小夥伴的,如果學過python,請直接移步到教程之源的gayhub,那裡寫的已經很清楚了。準備過程1.python 環境:mac 自帶python 2.x,或者在終端輸入 pythonlovesosoi$ pythonPython 2.7.10 (default, Jul 14 2015, 19:46:27)[GCC 4.2.1 Compatible Apple LLVM 6.0 (clang-600.0.39)] on darwinType "help"
  • 微信跳一跳小遊戲有毒
    然後好多人的微信頭像加了聖誕帽,不過我沒發朋友圈,我朋友圈很少發這些,大多發的是一些我喜歡看的文章,電影,公眾號和音樂。一開始的時候我還以為是真的,心想,微信牛逼啊,這也可以,微信應該是將發了這條朋友圈的人加入隊列來處理聖誕帽,看到越來越多人發,但是有些人並沒有頭像加聖誕帽,才意識到這些頭像都是自己 p 的,還有人在朋友賣p頭像加聖誕帽,服了,老鐵。
  • 「IT」人應如何面對微信『跳一跳』高分挑戰
    在微信「跳一跳」這個遊戲中,除了遊戲常見的好友排行榜,它還新增了超越提示——當你越過一個跳躍臺時,你會看到你正在超過某個好友。在這些超越的背後,其實玩家本身的社交地位也在上升,這與「拼點讚數多少」實際上如出一轍。而自我酬賞則是另一種內部心理訴求,他追求的是對事物的掌控感和滿足感。
  • "跳一跳"微信小程序,高分攻略在這裡!
    近期,微信最新版本 6.6.1 的更新開始,微信小程序遊戲「跳一跳」似乎在一夜之間進入了所有人的視野,甚至比五六年前的飛機大戰遊戲都火爆
  • 微信小遊戲「跳一跳」外掛揭秘:如何輕鬆得高分?
    自從17年12月28日微信上線了「跳一跳」小程序遊戲後我的朋友圈就沒有停止過
  • 跳一跳AI(wai gua)的實現原理詳細介紹
    V6.6.1新版本,新增了一系列小程序遊戲,主推遊戲「跳一跳」的每周更新排行榜也激也了一些玩家的刷分熱情,現在很多大牛在github上發布了程序代玩的各種語言的實現原始碼,那麼我們就來講講代碼的實現原理。
  • 新手Python實現微信跳一跳自動運行,再忍不住了
    最近我相信很多人都在玩微信的跳一跳小遊戲,前面幾天,很多人在朋友圈曬「跳一跳」人工智慧開掛教程:如何讓電腦自己玩微信跳一跳。
  • 我用Python玩小遊戲「跳一跳」,瞬間稱霸了朋友圈!
    以上是針對普通用戶,但對咱們程序猿來說用這套太 Low 了,接下來要說的是如何從技術層面去實現高分:在 Github 上面已經有人用 Python 來玩跳一跳這個遊戲了,想多少分就有多少分。界面轉至微信跳一跳遊戲,點擊開始遊戲。運行 python wechat_junp_auto.py,如果手機界面顯示 USB 授權,請點擊確認。很有趣!簡單點說就是:用電腦幫你玩微信跳一跳,全自動,不用手動。效果:
  • 微信小程序「跳一跳」3500的高分是怎麼來的?
    自從微信更新到了6.1.1版本,一款名為「跳一跳」的小程序風靡朋友圈。
  • 程序丨揭密微信《跳一跳》小遊戲那些外掛
    作者:Hahn, 騰訊高級UI工程師商業轉載請聯繫騰訊WeTest獲得授權,非商業轉載請註明出處。
  • 安卓遊戲蜂窩:跳一tiao輔助,免root腳本
    遊戲蜂窩app也出微信跳一跳遊戲輔助了,支持免root使用,直接上輸入想要跳的分數,設置解析度就可以跳了,超級簡單,這只是一個腳本,不知道蘋果的能不能用
  • 我們都被微信 「跳一跳」 套路了!
    不知道差友們最近有沒有玩兒 「 跳一跳 」 ?這小程序從上架那天起,就開始瘋狂洗刷我的票圈和各種微信群。整個朋友圈都在曬自己的分數( emmmm,好的,「 跳一跳 」 絕對是微信的親兒子,鑑定完畢。)誒嘿,人家開始賣廣告了!2000 萬!第一次就這麼給了 Nike!