【深入浅出强化学习-编程实战】2 马尔可夫决策过程-鸳鸯系统

news/2024/5/18 22:17:05 标签: 游戏, 游戏开发, python, 强化学习, 机器学习

【深入浅出强化学习-编程实战】2 马尔可夫决策过程

    • 2.1 鸳鸯系统马尔可夫决策过程
      • 2.1.1 代码展示
      • 2.1.2 部分代码解析

2.1 鸳鸯系统马尔可夫决策过程

左上为雄鸟,右上为雌鸟,中间有两道障碍物。目标:雄鸟找到雌鸟。

2.1.1 代码展示

main.py

python">import pygame
from resource.load import *
import math
import time
import random
import numpy as np

class YuanYangEnv:
    # 初始化子函数
    def __init__(self):
        # 状态空间
        self.states = []# 0-99
        for i in range(0,100):
            self.states.append(i)
        self.actions=['e','w','n','s']
        self.gamma = 0.8
        # 值函数
        self.value = np.zeros((10,10))# 10*10的表格

        # 设置渲染属性
        self.viewer = None # 一个渲染窗口
        # 帧速率是指程序每秒在屏幕山绘制图像的数目,我们可以用FPS来表示它。一般的计算机都能达到每秒60帧的速度。如果我们把帧速率讲得比较低,那么游戏也会看上去较为卡顿。
        self.FPSCLOCK = pygame.time.Clock()
        # 屏幕大小
        self.screen_size = (1200,900)
        # 雄鸟当前位置
        self.bird_position = (0,0)
        # 雄鸟在x方向每走一次像素为120
        self.limit_distance_x = 120
        # 雄鸟在y方向每走一次像素为90
        self.limit_distance_y = 90
        # 每个障碍物大小为120像素*90像素
        self.obstacle_size = [120,90]
        # 一共有两个障碍物墙,每个障碍物墙由8个小障碍物组成
        self.obstacle1_x = []
        self.obstacle1_y = []
        self.obstacle2_x = []
        self.obstacle2_y = []
        for i in range(8):
            # 第一个障碍物
            self.obstacle1_x.append(360)
            if i <= 3:
                self.obstacle1_y.append(90*i)
            else:
                self.obstacle1_y.append(90*(i+2))
            # 第二个障碍物
            self.obstacle2_x.append(720)
            if i <= 4:
                self.obstacle2_y.append(90*i)
            else:
                self.obstacle2_y.append(90*(i+2))
        # 雄鸟初始位置
        self.bird_male_init_position = [0,0]
        # 雄鸟当前位置
        self.bird_male_position = [0,0]
        # 雌鸟初始位置
        self.bird_female_init_position = [1080,0]

    # 雄鸟碰撞检测子函数
    def collide(self, state_position):
        # 用标志flag,flag1,flag2分别表示是否与障碍物、障碍物墙1、障碍物墙2发生碰撞
        flag = 1
        flag1 = 1
        flag2 = 1
        # 检测雄鸟是否与第一个障碍物墙发生碰撞
        # 找到雄鸟与第一个障碍物所有障碍物x方向和y方向最近的障碍物的坐标差
        # 并判断最近的坐标差是否大于一个最小运动距离
        # 如果大于等于 就不会发生碰撞
        dx = []
        dy = []
        for i in range(8):
            dx1 = abs(self.obstacle1_x[i] - state_position[0])
            dx.append(dx1)
            dy1 = abs(self.obstacle1_y[i] - state_position[1])
            dy.append(dy1)
        mindx = min(dx)
        mindy = min(dy)
        if mindx >= self.limit_distance_x or mindy >= self.limit_distance_y:
            flag1 = 0  # 没碰
        # 是否与第二个障碍物墙碰撞
        second_dx = []
        second_dy = []
        for i in range(8):
            dx2 = abs(self.obstacle2_x[i] - state_position[0])
            second_dx.append(dx2)
            dy2 = abs(self.obstacle2_y[i] - state_position[1])
            second_dy.append(dy2)
        mindx = min(second_dx)
        mindy = min(second_dy)
        if mindx >= self.limit_distance_x or mindy >= self.limit_distance_y:
            flag2 = 0  # 没碰
        if flag1 == 0 and flag2 == 0:
            flag = 0  # 没碰
        # 是否超出边界,如果是,也认为发生碰撞
        if state_position[0] > 1080 or state_position[0] < 0 or state_position[1] > 810 or state_position[1] < 0:
            flag = 1  # 碰了
        # 返回碰撞标志位
        return flag

    # 雄鸟是否找到雌鸟子函数
    def find(self,state_position):
        # 设置标志位flag
        # 判断雄鸟当前位置和雌鸟位置坐标差,雄安与最小运动距离则为找到
        flag = 0
        if abs(state_position[0] - self.bird_female_init_position[0]) < self.limit_distance_x and abs(
                state_position[1] - self.bird_female_init_position[1]) < self.limit_distance_y:
            flag = 1
        return flag

     # 状态转化为像素坐标子函数
    def state_to_position(self, state):
        i = int(state / 10)
        j = state % 10
        position = [0, 0]
        position[0] = 120 * j
        position[1] = 90 * i
        return position

    # 像素转化为状态坐标子函数
    def position_to_state(self, position):
        i = position[0] / 120
        j = position[1] / 90
        return int(i * 10 + j)

    # 重置环境子函数
    def reset(self):
        # 随机产生一个合法的初始位置,不能在障碍物处,不能在雌鸟处,
        flag1 = 1
        flag2 = 1
        while flag1 or flag2 == 1:
            # 随机产生初始状态0-99,random.random()产生一个0-1的随机数
            state = self.states[int(random.random()*len(self.states))]
            state_position = self.state_to_position(state)
            flag1 = self.collide(state_position)
            flag2 = self.find(state_position)
        return state


    # 状态转移子函数
    # 首先判断当前坐标是否与障碍物碰撞或是否是终点,如果是,结束转换
    # 如果否,更具运动学进行像素位置坐标的转换
    # 最后判断碰撞和是否找到
    # 没找到:回报为0;找到:回报为1;碰撞:回报为-1
    def transform(self,state,action):
        # 将当前状态转化为坐标
        current_position = self.state_to_position(state)
        next_position = [0,0]
        flag_collide = 0
        flag_find = 0
        # 判断是否与障碍物碰撞
        flag_collide = self.collide(current_position)
        # 判断是否为终点
        flag_find = self.find(current_position)
        if flag_collide==1 or flag_find ==1:
            return state,0,True # 结束
        # 状态转移
        if action == 'e':
            next_position[0]=current_position[0] + 120
            next_position[1]=current_position[1]
        if action == 'w':
            next_position[0] = current_position[0] - 120
            next_position[1] = current_position[1]
        if action == 'e':
            next_position[0] = current_position[0] + 120
            next_position[1] = current_position[1]
        if action == 'n':
            next_position[0] = current_position[0]
            next_position[1] = current_position[1] - 90
        if action == 's':
            next_position[0] = current_position[0]
            next_position[1] = current_position[1] + 90
        # 判断是否碰撞
        flag_collide = self.collide(next_position)
        # 如果碰撞,回报为-1,并结束
        if flag_collide==1:
            return self.position_to_state(current_position),-1,True # 结束
        # 判断是否为终点
        flag_find = self.find(next_position)
        if flag_find == 1:
            return self.position_to_state(next_position),1,True # 结束
        return self.position_to_state(next_position),0,False # 不结束

    # 游戏结束控制子函数
    # 调用pygame中的事件用来结束游戏,如果结束,则pygame会结束渲染
    def gameover(self):
        for event in pygame.event.get():
            if event.type == QUIT:
                exit()

    # 游戏渲染子函数
    def render(self):
        # 首先判断环境是否有一个游戏窗口,如果没有,调用pygame.display.set_mode设置一个游戏窗口
        # 调用load_bird_male(),load_bird_female(),load_background(),load_obstacle()函数下载游戏环境需要的突破 这四个函数的实现在load.py中进行
        # 下载完后,利用pygame自带的blit函数将图片画到窗口上
        if self.viewer is None:
            pygame.init()

            # 画一个窗口
            self.viewer = pygame.display.set_mode(self.screen_size,0,32)# 32表示深度
            pygame.display.set_caption("yuanyang")
            # 下载图片
            self.bird_male = load_bird_male()
            self.bird_female = load_bird_female()
            self.background = load_background()
            self.obstacle = load_obstacle()
            # self.viewer.blit(self.bird_male,self.bird_male_init_position)
            # 在幕布上画图片
            self.viewer.blit(self.bird_female,self.bird_female_init_position)
            self.viewer.blit(self.background,(0,0))
            self.font = pygame.font.SysFont('times',15)# 字体大小15

        # 如果环境中已经有窗口
        # 则依次画:背景图,11条纵向直线,11条横向直线,第一个障碍物墙,第二个障碍物墙,雄鸟,值函数
        # 画完后,调用pygame.display.update将这些原色更新到幕布中
        # 调用子函数gameover()来检查是否要结束有游戏
        self.viewer.blit(self.background,(0,0))
        # 画直线
        for i in range(11):
            pygame.draw.lines(self.viewer,(255,255,255),True,((120*i,0),(120*i,900)),1)
            pygame.draw.lines(self.viewer, (255, 255, 255), True, ((0, 90*i), (1200, 90*i)), 1)
        self.viewer.blit(self.bird_female,self.bird_female_init_position)
        # 画障碍物
        for i in range(8):
            self.viewer.blit(self.obstacle, (self.obstacle1_x[i], self.obstacle1_y[i]))
            self.viewer.blit(self.obstacle, (self.obstacle2_x[i], self.obstacle2_y[i]))

        # 画雄鸟
        self.viewer.blit(self.bird_male,self.bird_male_position)
        # 画值函数
        for i in range(10):
            for j in range(10):
                surface = self.font.render(str(round(float(self.value[i,j]),3)),True,(0,0,0))
                self.viewer.blit(surface,(120*i+5,90*j+70))
        pygame.display.update()
        self.gameover()
        self.FPSCLOCK.tick(30)

# 主函数程序测试
# 首先声明一个鸳鸯环境的示例yy
# 然后调用渲染函数将鸳鸯系统绘制出来
# 最后调用循环语句检查是都要结束游戏渲染
if __name__ == "__main__":
    yy = YuanYangEnv()
    yy.render()
    while True:
        for event in pygame.event.get():
            if event.type == QUIT:
                exit()

load.py

python">import pygame
import os
from pygame.locals import *
from sys import  exit
#得到当前工程目录
current_dir = os.path.split(os.path.realpath(__file__))[0]
print(current_dir)
#得到文件名
bird_file = r"F:\pythonProject\pythonProject\pythonProject8\resource\bird.png"
obstacle_file = r"F:\pythonProject\pythonProject\pythonProject8\resource\obstacle.png"
background_file = r"F:\pythonProject\pythonProject\pythonProject8\resource\background.png"
#创建小鸟,
def load_bird_male():
    bird = pygame.image.load(bird_file).convert_alpha()
    return bird
def load_bird_female():
    bird = pygame.image.load(bird_file).convert_alpha()
    return bird
#得到背景
def load_background():
    background = pygame.image.load(background_file).convert()
    return background
#得到障碍物
def load_obstacle():
    obstacle = pygame.image.load(obstacle_file).convert()
    return obstacle

文件放置位置如下:
在这里插入图片描述
图片:
obstacle.png
bird.png
background.png

结果展示:
在这里插入图片描述

2.1.2 部分代码解析

  1. line23
  • 帧速率是指程序每秒在屏幕山绘制图像的数目,我们可以用FPS来表示它。一般的计算机都能达到每秒60帧的速度。如果我们把帧速率讲得比较低,那么游戏也会看上去较为卡顿。
  • pygame.time.Clock对象帮助我们确定程序要以多少最大的帧速率运行,这个对象在游戏每一次迭代都会设置一个暂停,以防程序运行过快,有时候计算机的速度过于快速,我们就可以利用这个对象来让计算机在一个固定的速度运行,我们可以这样创建Clock对象:
python">FPSClock=pygame.time.Clock()
  • 我们要把他放在结尾pygame.display.update()后,我们可以用tick对象来让计算机知道你要在每一次迭代后暂停多少时间:
python">FPSClock.tick(30)
  1. line199
python">self.viewer = pygame.display.set_mode(self.screen_size,0,32)
  • 初始化一个准备显示的窗口或屏幕。
python">set_mode(resolution=(0,0), flags=0, depth=0) -> Surface
  • 这个函数将创建一个 Surface 对象的显示界面。传入的参数用于指定显示类型。最终创建出来的显示界面将是最大可能地匹配当前操作系统。
  • resolution 参数是一个二元组,表示宽和高。flags 参数是附件选项的集合。depth 参数表示使用的颜色深度。
  • 返回的 Surface 对象可以像常规的 Surface 对象那样去绘制,但发生的改变最终会显示到屏幕上。
  • 如果没有传入 resolution 参数,或者使用默认设置 (0, 0),且 Pygame 使用 SDL1.2.10 以上版本,那么创建出来的 Surface 对象将与当前屏幕用户一样的分辨率。如果只有宽或高其中一项被设置为 0,那么 Surface 对象将使用屏幕分辨率的宽或高代替它。如果 SDL 版本低于 1.2.10,那么将抛出异常。
  • 通常来说,最好的做法是不要传递 depth 参数。因为默认 Pygame 会根据当前操作系统选择最好和最快的颜色深度。如果你的游戏确实需要一个特殊的颜色格式,那么你可以通过控制 depth 参数来实现。Pygame 将为模拟一个非现成的颜色深度而耗费更多的时间。
  • 当使用全屏显示模式的时候,有时候无法完全匹配到需要的分辨率。在这种情况下,Pygame 将自动选择最匹配的分辨率使用,而返回的 Surface 对象将保持与需求的分辨率一致。
  • flags 参数指定你想要的显示类型。提供几个选择给你,你可以通过位操作同时使用多个类型(管道操作符 “|”)。如果你传入 0 或没有传入 flags 参数,默认会使用软件驱动窗口。这儿是 flags 参数提供的几个可选项:
    在这里插入图片描述
  1. line 210
python">self.font = pygame.font.SysFont('times',15)
  • 从系统字体库创建一个 Font 对象。
python">SysFont(name, size, bold=False, italic=False) -> Font
  • 从系统字体库中加载并返回一个新的字体对象。
  • 该字体将会匹配 bold(加粗)和 italic(斜体)参数的要求。
  • 如果找不到一个合适的系统字体,该函数将会回退并加载默认的 pygame 字体。
  • 尝试搜索的 name 参数可以是一个用逗号隔开的列表。
  1. line 219
python">pygame.draw.lines(self.viewer,(255,255,255),True,((120*i,0),(120*i,900)),1)
  • 原型:
python">pygame.draw.lines(Surface, color, closed, pointlist, width=1): return Rect
  • 用途:用于绘制一系列直线段。closed是一个布尔变量,如果closed为真,那么表示需要把第一点和最后一点连接起来。这些点来自pointlist,一个包含坐标点的列表。这个函数不会绘制线条的端点,也没有斜角连接(miter joints),而且角度小和线条粗的连线看起来会有点奇怪( Lines with sharp corners and wide line widths can have improper looking corners.)。
  1. line 232
python"> surface = self.font.render(str(round(float(self.value[i,j]),3)),True,(0,0,0))
  • 第一个参数是写的文字;第二个参数是个布尔值,是否开启抗锯齿,如果为True的话字体会比较平滑,不过相应的速度有一点点影响;第三个参数是字体的颜色;第四个是背景色,如果你想没有背景色(也就是透明),那么可以不加这第四个参数。
  • 其中
    round()函数用于返回浮点数number的四舍五入值
  • 语法:
python">round(number[, ndigits])
  • 参数介绍:
    number — 数值表达式
    ndigits — 表示从小数点位数,其中 number 需要四舍五入,默认值为 0
  • 返回值:
    返回浮点数number的四舍五入值

http://www.niftyadmin.cn/n/1399171.html

相关文章

【深入浅出强化学习-编程实战】3 基于动态规划的方法-鸳鸯系统

【深入浅出强化学习-编程实战】3 基于动态规划的方法2.1 鸳鸯系统——基于动态规划的方法2.1.1 基于策略迭代代码展示2.1.2 基于值函数迭代代码展示2.1.3 部分代码解析2.1 鸳鸯系统——基于动态规划的方法 左上为雄鸟&#xff0c;右上为雌鸟&#xff0c;中间有两道障碍物。目标…

【深入浅出强化学习-编程实战】3 基于蒙特卡洛的方法的鸳鸯系统

【深入浅出强化学习-编程实战】3 基于蒙特卡洛的方法3.1 鸳鸯系统——基于蒙特卡洛的方法3.2 部分代码思考3.1 鸳鸯系统——基于蒙特卡洛的方法 左上为雄鸟&#xff0c;右上为雌鸟&#xff0c;中间有两道障碍物。目标&#xff1a;雄鸟找到雌鸟。 在模型已知的时候&#xff0c…

【深入浅出强化学习-编程实战】4 基于时间差分的方法-鸳鸯系统

【深入浅出强化学习-编程实战】4 基于时间差分的方法4.1 鸳鸯系统——基于时间差分的方法4.2 Sarsa结果4.3 Q-learning结果4.1 鸳鸯系统——基于时间差分的方法 左上为雄鸟&#xff0c;右上为雌鸟&#xff0c;中间有两道障碍物。目标&#xff1a;雄鸟找到雌鸟。 yuanyang_env_…

【深入浅出强化学习-编程实战】6 基于函数逼近的方法-鸳鸯系统

【深入浅出强化学习-编程实战】6 基于函数逼近的方法-鸳鸯系统6.1 鸳鸯系统——基于函数逼近的方法基于表格表征表示基于固定稀疏表示代码困惑6.1 鸳鸯系统——基于函数逼近的方法 左上为雄鸟&#xff0c;右上为雌鸟&#xff0c;中间有两道障碍物。目标&#xff1a;雄鸟找到雌…

【深入浅出强化学习-编程实战】6 基于函数逼近的方法-flappy bird

【深入浅出强化学习-编程实战】6 基于函数逼近的方法-flappy bird6.2.1 代码6.2.2 代码解析玩家通过控制小鸟上下运动躲避不断到来的柱子&#xff0c;有两个动作可选&#xff1a;一个是飞&#xff0c;一个是不进行任何操作。采用动作飞时&#xff0c;小鸟会向上飞&#xff1b;不…

【深入浅出强化学习-编程实战】 7 基于策略梯度的强化学习-Cartpole(小车倒立摆系统)

【深入浅出强化学习-编程实战】 7 基于策略梯度的强化学习-Cartpole小车倒立摆MDP模型代码代码解析小车倒立摆MDP模型 状态输入&#xff1a;s[x,x˙,θ,θ˙]s [x,\dot{x},\theta,\dot{\theta}]s[x,x˙,θ,θ˙]&#xff0c;维数为4动作输出为a{[1,0],[0,1]}a \{[1,0],[0,1]…

【深入浅出强化学习-编程实战】 7 基于策略梯度的强化学习-Pendulum(单摆系统)

【深入浅出强化学习-编程实战】 7 基于策略梯度的强化学习-PendulumPendulum单摆系统MDP模型代码代码学习Pendulum单摆系统MDP模型 该系统只包含一个摆杆&#xff0c;其中摆杆可以绕着一端的轴线摆动&#xff0c;在轴线施加力矩τ\tauτ来控制摆杆摆动。Pendulum目标是&#x…

【深入浅出强化学习-编程实战】 8 Actor-Critic-Pendulum(单摆系统)

【深入浅出强化学习-编程实战】 8 Actor-Critic-PendulumPendulum单摆系统MDP模型TD-AC算法TD-AC算法代码解析Minibatch-MC-AC算法Minibatch-MC-AC算法代码解析Pendulum单摆系统MDP模型 该系统只包含一个摆杆&#xff0c;其中摆杆可以绕着一端的轴线摆动&#xff0c;在轴线施加…