基於Q-Learning 的FlappyBird AI
基於Q-Learning 的FlappyBird AI
在birdbot實現的FlappyBird基礎上訓練AI,這個FlappyBird的實現對遊戲進行了簡單的封裝,可以很方便得到遊戲的狀態來輔助演算法實現。同時可以顯示遊戲介面方便除錯,能夠看到演算法實現的效果。也可以選擇關閉遊戲介面以及聲音,這樣遊戲仍然能正常執行,一般用於訓練階段,可以減少CPU的佔用
實現參考的是SarvagyaVaish的Flappy Bird RL
Q-Learning
Q-Learning是強化學習演算法中value-based的演算法
Q即為Q(s,a)就是在某一時刻的 s 狀態下(s∈S),採取 動作a (a∈A)動作能夠獲得收益的期望,環境會根據agent的動作反饋相應的回報reward,所以演算法的主要思想就是將State與Action構建成一張Q-table來儲存Q值,然後根據Q值來選取能夠獲得最大的收益的動作
Q-Table | a1 | a2 |
---|---|---|
s1 | q(s1,a1) | q(s1,a2) |
s2 | q(s2,a1) | q(s2,a2) |
s3 | q(s3,a1) | q(s3,a2) |
演算法流程
在更新的過程中,引入了學習速率alpha,控制先前的Q值和新的Q值之間有多少差異被保留
γ為折扣因子,0<= γ<1,γ=0表示立即回報,γ趨於1表示將來回報,γ決定時間的遠近對回報的影響程度
詳細的Q-Learning過程可以參考下面這篇
A Painless Q-learning Tutorial (一個 Q-learning 演算法的簡明教程)
FlappyBird中應用
狀態空間
- 從下方管子開始算起的垂直距離
- 從下一對管子算起的水平距離
- 鳥:死或生
動作
每一個狀態,有兩個可能的動作
- 點選一下
- 啥也不幹
獎勵
獎勵的機制完全基於鳥是否存活
- +1,如果小鳥還活著
- -1000,如果小鳥死了
流程
虛擬碼
初始化 Q = {};
while Q 未收斂:
初始化小鳥的位置S,開始新一輪遊戲
while S != 死亡狀態:
使用策略π,獲得動作a=π(S)
使用動作a進行遊戲,獲得小鳥的新位置S',與獎勵R(S,a)
Q[S,A] ← (1-α)*Q[S,A] + α*(R(S,a) + γ* max Q[S',a]) // 更新Q
S ← S'
-
觀察Flappy Bird處於什麼狀態,並執行最大化預期獎勵的行動。然後繼續執行遊戲,接著獲得下一個狀態s’
-
觀察新的狀態s’和與之相關的獎勵:+1或者-1000
-
根據Q Learning規則更新Q陣列
Q[s,a] ← Q[s,a] + α (r + γ*V(s’) - Q[s,a])
-
設定當前狀態為s’,然後重新來過
程式碼
import pyglet
import random
import pickle
import atexit
import os
from pybird.game import Game
class Bot:
def __init__(self, game):
self.game = game
# constants
self.WINDOW_HEIGHT = Game.WINDOW_HEIGHT
self.PIPE_WIDTH = Game.PIPE_WIDTH
# this flag is used to make sure at most one tap during
# every call of run()
self.tapped = False
self.game.play()
# variables for plan
self.Q = {}
self.alpha = 0.7
self.explore = 100
self.pre_s = (9999, 9999)
self.pre_a = 'do_nothing'
self.absolute_path = os.path.split(os.path.realpath(__file__))[0]
self.memo = self.absolute_path + '/memo'
if os.path.isfile(self.memo):
_dict = pickle.load(open(self.memo))
self.Q = _dict["Q"]
self.game.record.iters = _dict.get("iters", 0)
self.game.record.best_iter = _dict.get("best_iter", 0)
def do_at_exit():
_dict = {"Q": self.Q,
"iters": self.game.record.iters,
"best_iter": self.game.record.best_iter}
pickle.dump(_dict, open(self.memo, 'wb'))
atexit.register(do_at_exit)
# this method is auto called every 0.05s by the pyglet
def run(self):
if self.game.state == 'PLAY':
self.tapped = False
# call plan() to execute your plan
self.plan(self.get_state())
else:
state = self.get_state()
bird_state = list(state['bird'])
bird_state[2] = 'dead'
state['bird'] = bird_state
# do NOT allow tap
self.tapped = True
self.plan(state)
# restart game
print 'iters:',self.game.record.iters,' score:', self.game.record.get(), 'best: ', self.game.record.best_score
self.game.record.inc_iters()
self.game.restart()
self.game.play()
# get the state that robot needed
def get_state(self):
state = {}
# bird's position and status(dead or alive)
state['bird'] = (int(round(self.game.bird.x)), \
int(round(self.game.bird.y)), 'alive')
state['pipes'] = []
# pipes' position
for i in range(1, len(self.game.pipes), 2):
p = self.game.pipes[i]
if p.x < Game.WINDOW_WIDTH:
# this pair of pipes shows on screen
x = int(round(p.x))
y = int(round(p.y))
state['pipes'].append((x, y))
state['pipes'].append((x, y - Game.PIPE_HEIGHT_INTERVAL))
return state
# simulate the click action, bird will fly higher when tapped
# It can be called only once every time slice(every execution cycle of plan())
def tap(self):
if not self.tapped:
self.game.bird.jump()
self.tapped = True
# That's where the robot actually works
# NOTE Put your code here
def plan(self, state):
x = state['bird'][0]
y = state['bird'][1]
if len(state['pipes']) == 0:
if y < self.WINDOW_HEIGHT / 2:
self.tap()
return
h, v = 9999, 9999
reward = -1000 if state['bird'][2] == 'dead' else 1
for i in range(1, len(state['pipes']), 2):
p = state['pipes'][i]
if x <= p[0] + self.PIPE_WIDTH:
h = p[0] + self.PIPE_WIDTH - x
v = p[1] - y
break
scale = 10
h /= scale
v /= scale
self.Q.setdefault((h, v), {'tap': 0, 'do_nothing': 0})
self.Q.setdefault(self.pre_s, {'tap': 0, 'do_nothing': 0})
tap_v = self.Q[(h, v)]['tap']
nothing_v = self.Q[(h, v)]['do_nothing']
self.Q[self.pre_s][self.pre_a] += self.alpha * (reward + max(tap_v, nothing_v) - self.Q[self.pre_s][self.pre_a])
self.pre_s = (h, v)
if random.randint(0, self.explore) > 100:
self.pre_a = "do_nothing" if random.randint(0, 1) else "tap"
else:
tap_v = self.Q[self.pre_s]['tap']
nothing_v = self.Q[self.pre_s]['do_nothing']
self.pre_a = "do_nothing" if tap_v <= nothing_v else "tap"
if self.pre_a == 'tap':
self.tap()
else:
pass
if __name__ == '__main__':
show_window = True
enable_sound = False
game = Game()
game.set_sound(enable_sound)
bot = Bot(game)
def update(dt):
game.update(dt)
bot.run()
pyglet.clock.schedule_interval(update, Game.TIME_INTERVAL)
if show_window:
window = pyglet.window.Window(Game.WINDOW_WIDTH, Game.WINDOW_HEIGHT, vsync = False)
@window.event
def on_draw():
window.clear()
game.draw()
pyglet.app.run()
else:
pyglet.app.run()
全部程式碼見github倉庫
參考
相關文章
- 基於RL(Q-Learning)的迷宮尋路演算法演算法
- 基於ReAct機制的AI AgentReactAI
- 【練習】canvas——flappyBirdCanvasAPP
- 基於SRAM的方法可加速AI推理AI
- [6] UE C++ FlappyBirdC++APP
- 基於lua協程的AI服務實現AI
- 基於 AI 大模型的精準測試分享AI大模型
- 帶你玩轉OpenHarmony AI-基於海思NNIE的AI能力自定義AI
- 基於yolov5實現的AI智慧盒子框架YOLOAI框架
- AI乾貨(一):為什麼說基於機器學習的AI預測更智慧?AI機器學習
- 學術派 | 基於AI的影片精彩度分析技術AI
- 為什麼說基於機器學習的AI預測更智慧?機器學習AI
- 基於 Kubernetes 的雲原生 AI 平臺建設AI
- E百科 | 基於MEC的邊緣AI服務AI
- 基於 SAP BTP 平臺的 AI 專案經驗分享AI
- AI生成遊戲中基於物理的渲染(PBR)貼圖探索AI遊戲
- 基於深度學習技術的AI輸入法引擎深度學習AI
- 基於AI知識庫RAG的綜合視窗系統AI
- 基於ChatGPT用AI實現自然對話ChatGPTAI
- 容器flappybird遊戲——圖文操作指引貼APP遊戲
- DQN(Deep Q-learning)入門教程(四)之Q-learning Play Flappy BirdAPP
- AI實戰分享 | 基於CANN的輔助駕駛應用案例AI
- 【AI in 美團】如何基於深度學習實現影象的智慧稽核?AI深度學習
- 使用基於 AI 的網路安全更快地檢測威脅AI
- 基於大模型的智慧體徹底顛覆AI應用大模型智慧體AI
- 基於AI的資料架構:業務在前,協作在後AI架構
- 輕鬆建立基於 GPT-4 的 AI 原生應用 - DifyGPTAI
- 基於釦子AI智慧體生成雙語播客AI智慧體
- Python程式碼實現“FlappyBird”小遊戲PythonAPP遊戲
- [Canvas前端遊戲開發]——FlappyBird詳解Canvas前端遊戲開發APP
- 基於AI排序演算法的指數增強策略【附原始碼】AI排序演算法原始碼
- 基於AI的英語學習應用WordUp推出"幻想聊天"功能AI
- 超越輔助:分享一個基於GPT引擎的免費AI工具GPTAI
- 基於AI+資料驅動的慢查詢索引推薦AI索引
- AiTracker.art:一個基於Torrent的AI模型分發器AI模型
- 用Go和Korok寫一個Flappybird遊戲-5GoAPP遊戲
- 使用深度學習進行基於AI的面部識別的不同方法深度學習AI
- 蘇寧基於 AI 和圖技術的智慧監控體系的建設AI