python吃豆人AI

Pac-Man

You need to program the Pac-Man game, and its screenshot is shown in
Figure 1. The player controls Pac-Man, who must eat all the dots inside an
enclosed maze while avoiding four colored ghosts. Eating large flashing
dots called “Power Pellets” causes the ghosts to temporarily turn blue,
allowing Pac-Man to eat them for bonus points.
Yor can search Pac-Man and try to play the game first.

运行截图

在这里插入图片描述

算法简述

目标是避开鬼吃掉所有豆子
每走一步循环如下语句:

  1. 若没有目标豆子, 选择一个曼哈顿距离最近的豆子作为目标豆子
  2. bfs寻找前往目标豆子最近的路, 不能吃的鬼视作障碍物
  3. 根据最短路径的选择前进方向前进

例如:
初始吃豆人坐标为 start: (14, 15)
目标豆子坐标为 focus: [11, 15]
计算出的最短路径为 result: [(14, 15), (13, 15), (12, 15), (11, 15)]
则更换吃豆人方向为向上
走了一步后,
start: (13, 15)
focus: [11, 15]
result: [(13, 15), (12, 15), (11, 15)]
吃豆人方向为向上, 继续前进

算法AI代码如下:

class AI:
    def __init__(self):
        self.focus_pac = None
        self.pacman_location = None
        self.first = True
        
    def action(self):
        self.pacman_location = [int(game.pacman.row), int(game.pacman.col)]
		
		# 如果没有focus, 寻找一个豆子为目标
        if not self.focus_pac:
            self.focus_pac = self.get_nearst_pac()

        # 判断focus的豆子被吃没有, 被吃后换新的豆子
        if gameBoard[self.focus_pac[0]][self.focus_pac[1]] == 1:
            self.focus_pac = self.get_nearst_pac()

        # 规划前往focus的路线
        global visited, path, result
        m, n = len(gameBoard), len(gameBoard[0])
        visited = [[False] * n for _ in range(m)]  # m * n 大小
        result = []  # 用于记录一条最短的路径,全局变量,格式和上面一样
        start = self.pacman_location[0], self.pacman_location[1]
        end = self.focus_pac[0], self.focus_pac[1]
        self.bfs(gameBoard, start, end)
        
        if result:
            # print("start: ", start)
            # print("focus: ", self.focus_pac)
            # print("result: ", result)
            if result[0][0] - result[1][0] > 0:
                return 0
            elif result[0][0] - result[1][0] < 0:
                return 2
            elif result[0][1] - result[1][1] < 0:
                return 1
            else:
                return 3
                
        # 如果寻路失败, 随机选择一个前进的方向
        else:
            return random.choice([0,1,2,3])
    
    # 寻找与吃豆人曼哈顿距离最近的豆子作为目标豆子
    def get_nearst_pac(self):
        direction = 100
        nearst_pac = []
        for row in range(len(gameBoard)):
            for col in range(len(gameBoard[0])):
                if gameBoard[row][col] == 2 or gameBoard[row][col] == 5 or gameBoard[row][col] == 6:
                    d = abs(self.pacman_location[0] - row) + abs(self.pacman_location[1] - col)
                    if d < direction:
                        nearst_pac = [row, col]
                        direction = d
        return nearst_pac

    # 寻找最短路径的BFS
    def bfs(self, maze, start, end):
        global visited, path, result
        m, n = len(gameBoard), len(gameBoard[0])
        queue = Queue()  # 创建一个队列,用于广度遍历
        father = {}  # 用于记录每个单元格的上一步单元格
        x, y = start  # 起点,取出(x, y)方便后面使用
        queue.put(start)  # 将起点放入队列
        visited[x][y] = True  # 将起点标记为已访问
        father[(x, y)] = None  # 起点没有上一步,所以标记为None
        while not queue.empty():  # 队列不为空时就循环
            cur = queue.get()  # 访问出队列中的第一个元素(同时具有读取和删除的功能)
            x, y = cur  # 解包出(x, y)方便后面使用
            visited[x][y] = True  # 将当前位置标记为已访问
            if cur == end:  # 如果当前位置是终点,就结束搜索
                # 根据father字典,从终点往回找,找到起点为止,就是一条最短路径
                while cur is not None:
                    result.append(cur)
                    cur = father[cur]
                result = result[::-1]  # 反转一下,从起点到终点
                return  # 返回,结束搜索
            for next_pos in [(x + 1, y), (x - 1, y), (x, y + 1), (x, y - 1)]:  # 遍历当前位置的上下左右四个方向
                x_next, y_next = next_pos  # 解包出(x_next, y_next)方便后面使用
                # 判断是否越界,是否是障碍物,是否已经访问过
                if 0 <= x_next < m and 0 <= y_next < n and maze[x_next][y_next] != 3 and not visited[x_next][y_next] and not self.has_ghost(x_next, y_next):
                    queue.put(next_pos)  # 将下一个可行位置加入队列
                    father[next_pos] = cur  # 将下一个可行位置的上一步单元格(下一个的上一个,就是当前单元格)记录下来

    def has_ghost(self, new_row, new_col):
        ghost_positions = []
        for ghost in game.ghosts:
            if not ghost.attacked:
                ghost_positions.append([ghost.row, ghost.col])

        for ghost_position in ghost_positions:
            if int(new_row) == int(ghost_position[0]) and int(new_col) == int(ghost_position[1]):
                return True
        return False
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值