【game】1、pacman利用bfs进行搜索路径自动吃豆

1.设计思路

设计思路有几个,一步步优化来的

v0.1 比较复杂,而且进行了2次bfs,浪费了大量时间

【game】1、pacman利用bfs进行搜索路径自动吃豆

v0.2 简化了2次bfs的操作,但是有很多不必要的判断逻辑,并且考虑不够全

【game】1、pacman利用bfs进行搜索路径自动吃豆

v0.3 极大简化了逻辑,并对幽灵,玩家的路径进行探索

【game】1、pacman利用bfs进行搜索路径自动吃豆

2.编码实现

这里只提供玩家实现,不提供主程序

import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;

/**
 * 人类玩家,使用运行时输入的方式
 */
public class CutterPlayer extends PacmanPlayer {
    private int nextPosition;
    private final String jarName;
    // 当前行,列
    private int position[] = new int[2];
    // 方向
    private final static int[][] DIR = {{0, -1}, {-1, 0}, {0, 1}, {1, 0}};
    public static final int BOARD_SIZE = 21; // 地图尺寸
    private static final int LEFT = 1; //
    private static final int UP = 2; //
    private static final int RIGHT = 3; //
    private static final int DOWN = 4; //
    private static final int MY_BARRIER = -999;
    private static final int DEEP = 21;
    private static final int CAN_NOT_ARRVIDE = -5;
    private static final int CAN_NOT_EAT = -5;
    private static final int CAN_NOT_ARRVIDE_MYSELF = -2;

    public CutterPlayer(String jarname) throws Exception {
        this.jarName = jarname;
    }

    @Override
    public void init() {
        super.init();
        nextPosition = 0;
    }

    @Override
    public String getName() {
        return jarName;
    }

    @Override
    public PlayerType getType() {
        return PlayerType.JAVA;
    }

    @Override
    public void ready(int no, int timeLimit) throws Exception {
        position[0] = no / 21;
        position[1] = no % 21;
        System.out.println("init[" + position[0] + ":" + position[1] + "]");
    }

    /**
     * 该接口在游戏中每回合都会被调用一次,用于告知选手程序当前局面,选手程序自行决定行动。
     *
     * @param board 局面的位置列表,假设a行b列(0=0 为该位置的pacman所代表的力量(包括一个自己)
     * @param strength 当前的力量,由服务器传回来
     * @return 方向0代表不动,1,2,3,4 分别代表左、上、右、下,其他输入皆非法
     * @throws Exception
     */
    @Override
    public int run(int[] board, int strength) throws Exception {

        // board转换为二维数组
        boolean haveTarget = false;
        int[][] erweiboard = new int[BOARD_SIZE][BOARD_SIZE];
        for (int i = 0; i < board.length; ++i) {
            if (board[i] > -5) haveTarget = true;
            if (isCannotArrivederweiboard(erweiboard, i)) {
                continue;
            }
            // 2.判断是否是幽灵
            // 3.判断是否是玩家
            // 3.1 玩家力量是否比自己小X
            if (isGhost(board, i) || isStrongPlayer(board, i, strength)) {
                setCannotArrived(erweiboard, i);
                continue;
            }
            // 4.计算当前二维数组位置
            // 5.填充进入数组
            erweiboard[i/21][i%21] = board[i];
        }

        if (!haveTarget) return 0;

        // 6.获取当前需要递归层数
        // 7.添加当前位置加入遍历队列
        Queue nextSteps = new LinkedList<>();  // 广度的队列
        boolean[][] flag = new boolean[BOARD_SIZE][BOARD_SIZE]; // 标记
        flag[position[0]][position[1]] = true;
        Node root = new Node(new int[] {position[0], position[1]});
        root.curScore = -999;
        nextSteps.offer(root);
        // 8.初始化当前层级
        int curdeep = 0;
        Node maxNode = null;
        boolean init = true;
        // 9.设置当前最大得分节点为当前节点,分值为-999
        // 10.判断队列是否为空&&当前层级
        while (!nextSteps.isEmpty() && curdeep null || init || maxNode.curScore != -2)) {
            // 11.循环单当前队列长度所有节点
            for (int i = 0; i < nextSteps.size(); i++) {
                Node sonNode = nextSteps.poll();
                int[] next = sonNode.value;
                // 遍历这个点的4个方向
                // 12.获取当前循环节点的方向&位置
                for (int j = 1; j < 5; j++) {
                    int[] temp = getNextDirection(j, next);
                    // 13.是否遍历过
                    // 14.是否识别为障碍
                    if (!flag[temp[0]][temp[1]] && erweiboard[temp[0]][temp[1]] > CAN_NOT_ARRVIDE) {
//                        System.out.println("erweiboard[" +temp[0]+ "][" + temp[1] + "]:" + erweiboard[temp[0]][temp[1]]);
                        // 15.创建新的路径节点
                        Node node = new Node(temp);
                        node.root = sonNode;
                        node.direction = j;
                        if (erweiboard[temp[0]][temp[1]] == -2) {
                            // 如果是-2 不可取
                            node.curScore = CAN_NOT_EAT;
                        } else {
                            node.curScore = erweiboard[temp[0]][temp[1]];
                        }
                        // 16.判断这个节点是否比当前最大得分节点还高
                        // 16.1 更新最大得分节点
                        if (maxNode == null || node.curScore > maxNode.curScore) {
                            maxNode = node;
                            init = false;
                        }
                        // 17.加入队列
                        nextSteps.offer(node);
                        flag[temp[0]][temp[1]] = true;
                    }
                }
            }
            // 18.循环结束,层级++
            curdeep++;
        }

        // 19.方向并查集向上求得路径最后一个父节点是当前位置的节点
        while (maxNode != null && maxNode.root != null && !Arrays.equals(maxNode.root.value, position)) {
            maxNode = maxNode.root;
        }
        // 20.是否为空,为空return 0
        if (maxNode == null || Arrays.equals(position, maxNode.value)) return 0;
        // 21.更新当前坐标
        position = getNextDirection(maxNode.direction, position);
        // 22.返回方向
        System.out.println("direction:" + maxNode.direction + "=>[" + position[0] + ":" + position[1] + "], score:" + maxNode.curScore);
        return maxNode.direction;

    }

    public static int[] getNextDirection(int direction, int[] prePosition) {
        int[] newPosition = new int[] {prePosition[0], prePosition[1]};
        switch (direction) {
            case UP:
                // 如果是顶部,则跳转到底部,其他方向同理,边界是打通的。
                if (prePosition[0] == 0) {
                    newPosition[0] = BOARD_SIZE - 1;
                } else {
                    newPosition[0] = prePosition[0] - 1;
                }
                break;
            case DOWN:
                if (prePosition[0] == BOARD_SIZE - 1) {
                    newPosition[0] = 0;
                } else {
                    newPosition[0] = prePosition[0] + 1;
                }
                break;
            case LEFT:
                if (prePosition[1] == 0) {
                    newPosition[1] = BOARD_SIZE - 1;
                } else {
                    newPosition[1] = prePosition[1] - 1;
                }
                break;
            case RIGHT:
                if (prePosition[1] == BOARD_SIZE - 1) {
                    newPosition[1] = 0;
                } else {
                    newPosition[1] = prePosition[1] + 1;
                }
                break;
            case 0:
                break; // 不动代表新位置还是原来的位置
            default: // 错误输入
                throw new RuntimeException("输入错误");
        }
        return newPosition;
    }

    private boolean isCannotArrived(int[] board, int site) {
        if (board[site] < CAN_NOT_ARRVIDE) {
            return true;
        }

        return false;
    }

    private boolean isCannotArrivederweiboard(int[][] erweiboard, int site) {
        int[] tmpposition = new int[2];
        tmpposition[0] = site / 21;
        tmpposition[1] = site % 21;
        if (erweiboard[tmpposition[0]][tmpposition[1]] != 0 || Arrays.equals(tmpposition, position)) {
            return true;
        }

        return false;
    }

    private boolean isGhost(int[] board, int site) {
        if (board[site] == -1) {
            return true;
        }

        return false;
    }

    private boolean isStrongPlayer(int[] board, int site, int strength) {
        if (board[site] >= 0 && board[site] >= strength - 2) {
            return true;
        }

        return false;
    }

    private void setCannotArrived(int[][] erweiboard, int site) {
        // 设置4个方向都不可达
        int[] prePosition = new int[2];
        prePosition[0] = site / 21;
        prePosition[1] = site % 21;

        for (int direction = 1; direction direction) {
            int[] newPosition = new int[] {prePosition[0], prePosition[1]};
            switch (direction) {
                case UP:
                    // 如果是顶部,则跳转到底部,其他方向同理,边界是打通的。
                    if (prePosition[0] == 0) {
                        newPosition[0] = BOARD_SIZE - 1;
                    } else {
                        newPosition[0] = prePosition[0] - 1;
                    }
                    break;
                case DOWN:
                    if (prePosition[0] == BOARD_SIZE - 1) {
                        newPosition[0] = 0;
                    } else {
                        newPosition[0] = prePosition[0] + 1;
                    }
                    break;
                case LEFT:
                    if (prePosition[1] == 0) {
                        newPosition[1] = BOARD_SIZE - 1;
                    } else {
                        newPosition[1] = prePosition[1] - 1;
                    }
                    break;
                case RIGHT:
                    if (prePosition[1] == BOARD_SIZE - 1) {
                        newPosition[1] = 0;
                    } else {
                        newPosition[1] = prePosition[1] + 1;
                    }
                    break;
                case 0:
                    break; // 不动代表新位置还是原来的位置
                default: // 错误输入
                    throw new RuntimeException("输入错误");
            }
            erweiboard[newPosition[0]][newPosition[1]] = MY_BARRIER;
            erweiboard[prePosition[0]][prePosition[1]] = CAN_NOT_ARRVIDE;
        }
    }

    /**
     * 用于保存路径
     */
    public static class Node {
        private int[] value;

        private int direction;

        private Node root;

        private int curScore = 0;

        public Node(int[] value) {
            this.value = value;
        }

        @Override
        public boolean equals(Object obj) {
            if (obj instanceof Node) {
                Node other = (Node) obj;
                return this.value[0] == other.value[0] && this.value[1] == other.value[1];
            }

            return super.equals(obj);
        }
    }

    public int getNextPosition() {
        return nextPosition;
    }

    public void setNextPosition(int nextPosition) {
        this.nextPosition = nextPosition;
    }

}

实现效果:

黄色为程序吃豆人,红色为外挂吃豆人(只做演示用,或者追杀黄色)

【game】1、pacman利用bfs进行搜索路径自动吃豆

Original: https://www.cnblogs.com/cutter-point/p/14258901.html
Author: cutter_point
Title: 【game】1、pacman利用bfs进行搜索路径自动吃豆

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/713176/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 看完您如果还不明白 Kerberos 原理,算我输!

    系统环境 操作系统:CentOS 6 或 CentOS 7 JDK 版本:1.8.0_151 Ambari 版本:2.6.1 HDP 版本:2.6.4.0 扩展链接 Kerbero…

    技术杂谈 2023年7月25日
    099
  • 51单片机——蓝牙远程点灯

    一、HC-05蓝牙模块 HC-05是主从一体的蓝牙串口模块,使用起来非常之方便,当HC-05与其他蓝牙设备连接成功后,使用方法与串口没什么差别,当然HC-05要跟你的单片机串口连接…

    技术杂谈 2023年5月31日
    0105
  • 在Windows Server 2022 上安装 容器主机(Containers)

    容器用于从小型应用程序运行到大型软件。容器主机是 Docker 守护程序和 Docker 客户端上运行的操作系统。我们将使用最新版本的 Window Server 2022,下面我…

    技术杂谈 2023年5月31日
    092
  • 国内镜像源对比

    Ubuntu、Python、Nodejs、MySQL、Git、Chromium、Docker、Homebrew 等一系列的常用最推荐的镜像源。 可能是最好的国内镜像站,最吸引人的特…

    技术杂谈 2023年5月31日
    098
  • 解决Win10锁屏超1分钟,显示器关闭问题

    Win10台式机锁定屏幕1分钟后,系统就会自动关闭显示器。从节能来说是非常不错的,但这不符合大多数人的电脑使用习惯,一会儿就黑屏了看起来不舒服。怎么修改这个系统默认设置呢?解决方法…

    技术杂谈 2023年6月1日
    0126
  • Uniscribe文字自动换行

    我们获得了每个字形的宽度数组piAdvances,以及这个RUN所占用的总宽度abc。 piVdvances对应于每个字符,它取得了每个字形所占用宽度。 如果我们以行为单位来绘制文…

    技术杂谈 2023年5月31日
    080
  • Go学习第一天:有关环境变量及结构的解释

    环境变量 有三个变量 GOPATH、 PATH、 GOROOT: GOROOT 就是 go 的安装路径; GOPATH 就是go的项目目录; PATH是go安装路径下的bin目录。…

    技术杂谈 2023年7月24日
    077
  • 千古前端图文教程-HTML006-HTML标签:图片标签

    HTML标签:图片标签 HTML标签:图片标签 img标签介绍 #介绍 能插入的图片类型 img标签的 src属性 #写法一:图片的相对路径 写法二:图片的绝对路径 #相对路径和绝…

    技术杂谈 2023年7月11日
    0107
  • PHPExcel升级为PhpSpreadsheet的注意事项

    PHPExcel升级为PhpSpreadsheet的注意事项 \PHPExcel_Shared_Date::ExcelToPHPObject() 升级为 \PhpOffice\Ph…

    技术杂谈 2023年5月31日
    0109
  • Func<T>、Action<T> 的区别于说明

    Func是一个.Net内置的委托。 Func Func Func Func Func Func 它有5种形式,只是参数个数不同;第一个是无参数,但是有返回值; 下面是一个简单的普通…

    技术杂谈 2023年5月31日
    0104
  • 8086汇编 cmp 指令

    8086汇编 cmp 指令 cmp 是比较指令,功能相当于减法指令,只是不保存结果。cmp 指令执行后,将对标志寄存器产生影响。 格式:cmp 操作对象1,操作对象2功能:计算操作…

    技术杂谈 2023年6月1日
    094
  • Spring事务(四)-事务失效场景

    有时候,我们明明在类或者方法上添加了 @Transactional注解,却发现方法并没有按事务处理。其实,以下场景会导致事务失效。 1、事务方法所在的类没有加载到Spring IO…

    技术杂谈 2023年7月11日
    080
  • LiteFlow 2.6.4版本发行注记,里程碑版本!

    一 这个版本做的很折腾。期间几个issue推翻重做了好几次。 但我最终还是带来了LiteFlow 2.6.4这个重要版本。 虽然版本是小版本号升级,但是带来的更新可一点也不少。并完…

    技术杂谈 2023年7月11日
    085
  • MySQL-连接数据库

    连接数据库在操作数据库之前,需要连接它,输入命令:mysql -u用户名 -p密码。 在你自己本机上连接数据库用上述方式是可以的,不过在平台上连接数据库还需要加上一句-h127.0…

    技术杂谈 2023年7月11日
    076
  • ES6 Template Strings(转)

    Strings in JavaScript have been historically limited, lacking the capabilities one might e…

    技术杂谈 2023年5月30日
    0100
  • JAVA设计模式-代理模式

    JAVA设计模式-代理模式 一、介绍 代理模式是一种结构型模式,它指的是给某一个对象提供一个代理对象,并且由代理对象控制原有对象的引用,可以增强原有对象的功能以及降低系统的耦合度。…

    技术杂谈 2023年6月21日
    0102
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球