用Python玩任一款街機遊戲!
編譯 Bot 來源 GitHub
編者按:近年來,雖然關於強化學習進展的新聞屢見報端,對強化學習感興趣的人也很多,但對普通學習者來說,真正做一個自己感興趣的強化學習專案還是太麻煩了。今天論智給大家推薦的是一名網友開源的Python庫,它提供了一個可以玩任何舊版街機遊戲的API,操作方式非常親民。
這是一個允許你在幾乎任何街機遊戲中訓練你的強化學習演算法的Python庫,它目前在Linux系統上可用。通過這個工具包,你可以定製演算法逐步完成遊戲過程,同時接收每一幀的資料和內部儲存器地址值以跟蹤遊戲狀態,以及傳送與遊戲互動的動作。
安裝
GitHub地址:github.com/M-J-Murray/MAMEToolkit/blob/master/README.md
你可以用pip安裝這個庫,只需執行以下命令:
pip install MAMEToolkit
演示示例:街霸
在街機愛好者心中,街霸是史上最經典的遊戲之一。現在工具包內包含的街霸版本是街頭霸王3:三度衝擊(Japan 990608, NO CD),我們以此為例,用以下程式碼寫一個隨機智慧體:
-
import random
-
from MAMEToolkit.sf_environment import Environment
-
roms_path = "roms/"
-
env = Environment("env1", roms_path)
-
env.start()
-
while True:
-
move_action = random.randint(0, 8)
-
attack_action = random.randint(0, 9)
-
frames, reward, round_done, stage_done, game_done = env.step(move_action, attack_action)
-
if game_done:
-
env.new_game()
-
elif stage_done:
-
env.next_stage()
-
elif round_done:
-
env.next_round()
這個工具包還支援hogwild!訓練:
什麼是hogwild!?Niu等人引入了一個叫做 Hogwild! 的更新策略,可以使 SGD 可以在多 CPU 上並行更新。處理器在無需對引數加鎖的情況下就可以訪問共享記憶體。但僅在輸入的是稀疏資料時才有效,因為每次更新僅修改所有引數的一小部分。他們展示了在這種情況下,更新策略幾乎可以達到一個最優的收斂率,因為處理器不太可能覆蓋掉有用的資訊。
-
from threading import Thread
-
import random
-
from MAMEToolkit.sf_environment import Environment
-
def run_env(env):
-
env.start()
-
while True:
-
move_action = random.randint(0, 8)
-
attack_action = random.randint(0, 9)
-
frames, reward, round_done, stage_done, game_done = env.step(move_action, attack_action)
-
if game_done:
-
env.new_game()
-
elif stage_done:
-
env.next_stage()
-
elif round_done:
-
env.next_round()
-
def main():
-
workers = 8
-
# Environments must be created outside of the threads
-
roms_path = "roms/"
-
envs = [Environment(f"env{i}", roms_path) for i in range(workers)]
-
threads = [Thread(target=run_env, args=(envs[i], )) for i in range(workers)]
-
[thread.start() for thread in threads]
建立自己的遊戲環境
這個工具包之所以易於上手,是因為它和模擬器本身不需要太多互動,只需注意兩點——一是查詢你關注的內部狀態相關聯的記憶體地址值,二是用選取的環境跟蹤狀態。你可以用MAME Cheat Debugger,它會反饋遊戲的記憶體地址值如何隨時間變化。如果要建立遊戲模擬,你得先獲得正在模擬的遊戲的ROM,並知道MAME使用的遊戲ID,比如街霸的ID是'sfiii3n'。
遊戲ID
你可以通過執行以下程式碼找到遊戲的ID:
from MAMEToolkit.emulator import Emulator emulator = Emulator("env1", "", "", memory_addresses)
這個命令會開啟MAME模擬器。你可以搜尋遊戲列表以找到想要的遊戲,遊戲的ID位於遊戲標題末尾的括號中。
記憶體地址
如果獲得了ID,也有了想要跟蹤的記憶體地址,你可以開始模擬:
-
from MAMEToolkit.emulator import Emulator
-
from MAMEToolkit.emulator import Address
-
roms_path = "roms/"
-
game_id = "sfiii3n"
-
memory_addresses = {
-
"fighting": Address('0x0200EE44', 'u8'),
-
"winsP1": Address('0x02011383', 'u8'),
-
"winsP2": Address('0x02011385', 'u8'),
-
"healthP1": Address('0x02068D0B', 's8'),
-
"healthP2": Address('0x020691A3', 's8')
-
}
-
emulator = Emulator("env1", roms_path, "sfiii3n", memory_addresses)
這會啟動模擬器,並在工具包連線到模擬器程序時暫停。
分步執行模擬器
連線工具箱後,你可以分步執行模擬器:
-
data = emulator.step([])
-
frame = data["frame"]
-
is_fighting = data["fighting"]
-
player1_wins = data["winsP1"]
-
player2_wins = data["winsP2"]
-
player1_health = data["healthP1"]
-
player2_health = data["healthP2"]
step函式會把幀資料作為NumPy矩陣返回,同時,它也會返回該時間步長的所有記憶體地址整數值。
如果要向模擬器輸入動作,你還需要確定遊戲支援的輸入埠和欄位。比如玩街霸需要先投幣,這個程式碼是:
-
from MAMEToolkit.emulator import Action
-
insert_coin = Action(':INPUTS', 'Coin 1')
-
data = emulator.step([insert_coin])
要確定哪些埠可用,請使用list actions命令:
-
from MAMEToolkit.emulator import list_actions
-
roms_path = "roms/"
-
game_id = "sfiii3n"
-
print(list_actions(roms_path, game_id))
下面這個返回的列表就包含街霸環境中可用於向步驟函式傳送動作的所有埠和欄位:
[ {'port': ':scsi:1:cdrom:SCSI_ID', 'field': 'SCSI ID'}, {'port': ':INPUTS', 'field': 'P2 Jab Punch'}, {'port': ':INPUTS', 'field': 'P1 Left'}, {'port': ':INPUTS', 'field': 'P2 Fierce Punch'}, {'port': ':INPUTS', 'field': 'P1 Down'}, {'port': ':INPUTS', 'field': 'P2 Down'}, {'port': ':INPUTS', 'field': 'P2 Roundhouse Kick'}, {'port': ':INPUTS', 'field': 'P2 Strong Punch'}, {'port': ':INPUTS', 'field': 'P1 Strong Punch'}, {'port': ':INPUTS', 'field': '2 Players Start'}, {'port': ':INPUTS', 'field': 'Coin 1'}, {'port': ':INPUTS', 'field': '1 Player Start'}, {'port': ':INPUTS', 'field': 'P2 Right'}, {'port': ':INPUTS', 'field': 'Service 1'}, {'port': ':INPUTS', 'field': 'Coin 2'}, {'port': ':INPUTS', 'field': 'P1 Jab Punch'}, {'port': ':INPUTS', 'field': 'P2 Up'}, {'port': ':INPUTS', 'field': 'P1 Up'}, {'port': ':INPUTS', 'field': 'P1 Right'}, {'port': ':INPUTS', 'field': 'Service Mode'}, {'port': ':INPUTS', 'field': 'P1 Fierce Punch'}, {'port': ':INPUTS', 'field': 'P2 Left'}, {'port': ':EXTRA', 'field': 'P2 Short Kick'}, {'port': ':EXTRA', 'field': 'P2 Forward Kick'}, {'port': ':EXTRA', 'field': 'P1 Forward Kick'}, {'port': ':EXTRA', 'field': 'P1 Roundhouse Kick'}, {'port': ':EXTRA', 'field': 'P1 Short Kick'} ]
模擬器類還有一個frame_ratio引數,可用於調整演算法所見的幀速率。預設情況下,MAME以每秒60幀的速度生成幀,如果你覺得這太多了,想把它改成每秒20幀,可以輸入以下程式碼:
-
from MAMEToolkit.emulator import Emulator
-
emulator = Emulator(roms_path, game_id, memory_addresses, frame_ratio=3)
MAME效能基準測試
目前這個工具包的開發和測試已在8核AMD FX-8300 3.3GHz CPU以及3GB GeForce GTX 1060 GPU上完成。在使用單個隨機智慧體的情況下,街頭霸王環境可以以正常遊戲速度的600%+執行。而如果是用8個隨機智慧體進行hogwild!訓練,環境可以以正常遊戲速度的300%+執行。
ConvNet智慧體
為了確保工具包能夠訓練演算法,作者還設定了一個簡單的5層ConvNet,只需少量調整,你就可以用它進行測試。在街霸實驗中,這個演算法能夠成功學習到遊戲的一些簡單技巧,比如連擊(combo)和格擋(blocking)。街霸本身的遊戲機制是分成10個關卡(難度遞增),玩家在每個關卡都要迎戰不同的對手。剛開始的時候,這個智慧體平均只能打到第2關。但在經過2200次訓練後,它平均能打到第5關。
至於智慧體的學習率,它是用每一局智慧體所造成的淨傷害和所承受的傷害來計算的。
- The End -
「若你有原創文章想與大家分享,歡迎投稿。」
加編輯微信ID,備註#投稿#:
程式 丨 druidlost
2018 中國大資料技術大會
◆
BDTC 2018
◆
BDTC 2018中國大資料技術大會攜主題“大資料新應用”再度強勢來襲。本次大會由 華東師範大學副校長、教授周傲英,百度商業智慧實驗室主任熊輝,阿里巴巴副總裁李飛飛 三位會議主席對大會內容把關,多位兩院院士參與指導,由最瞭解行業痛點的一線從業者為同行打造。
掃描下方二維碼或點選【閱讀原文】快速購票 。現在購票還有機會獲得大資料圖書一本(中國科學院院士梅巨集主編的《大資料導論》或華中科技大學教授金海主編的《大資料處理》),數量有限!
推薦閱讀:
-
SDN_bang/article/details/84529143" rel="nofollow,noindex" target="_blank">只瞭解View的事件分發是不夠的,來看下輸入系統對事件的處理
-
Blockchain_lemon/article/details/84528925" rel="nofollow,noindex" target="_blank">31位圈內大佬解讀DApp困惑:“爆款”也難優秀!
print_r('點個贊吧'); var_dump('點個贊吧'); NSLog(@"點個贊吧!"); System.out.println("點個贊吧!"); console.log("點個贊吧!"); print("點個贊吧!"); printf("點個贊吧!\n"); cout << "點個贊吧!" << endl; Console.WriteLine("點個贊吧!"); fmt.Println("點個贊吧!"); Response.Write("點個贊吧"); alert(’點個贊吧’)