AStart类
package com.company;
import java.util.ArrayList;
import java.util.List;
class AStar {
public static int[][] NODES;//定义一个迷宫单元数组
public int STEP = 10;//设每一步的权值为10
private ArrayList<Node> openList = new ArrayList<Node>();//open list存放已知但还没有探索过的区块
private ArrayList<Node> closeList = new ArrayList<Node>();//close list存放已经探索过的区块。
AStar(int[][] map) {
NODES=map;//初始化迷宫单元为新生成的对应地图,把Maze类里面生成的地图传给NODES,再在此地图基础上用A*算法寻路径
Node startNode = new Node(1, 1);//起点
Node endNode = new Node(map.length-2, map.length-2);//终点
Node parent = findPath(startNode, endNode); //父节点
ArrayList<Node> arrayList = new ArrayList<Node>();
while (parent != null) {
arrayList.add(new Node(parent.x, parent.y));
parent = parent.parent;
}
//打印有路径的地图,在控制台输出查看
//System.out.println("\n"+"打印有路径的地图:");
for (int i = 0; i < NODES.length; i++) {
for (int j = 0; j < NODES.length; j++) {
if (exists(arrayList, i, j)) {
NODES[i][j]=2;//标记关闭列表里的方格为2,为了方便后面在界面画系统寻路路径
}
//System.out.print(NODES[i][j] + " ");
}
//System.out.println();
}
}
public static int[][] ans(){
return NODES;
}
//寻找开放列表里F值最小的节点的方法
public Node findMinFNodeInOpenList() {
Node tempNode = openList.get(0);
for (Node node : openList) {
if (node.F < tempNode.F) {
tempNode = node;
}
}
return tempNode;
}
//遍历当前节点上下左右四个邻居的方法,
public ArrayList<Node> findNeighborNodes(Node currentNode) {
ArrayList<Node> arrayList = new ArrayList<Node>();
// 只考虑上下左右,不考虑斜对角
int topX = currentNode.x;
int topY = currentNode.y - 1;
if (canReach(topX, topY) && !exists(closeList, topX, topY)) {
arrayList.add(new Node(topX, topY));
}
int bottomX = currentNode.x;
int bottomY = currentNode.y + 1;
if (canReach(bottomX, bottomY) && !exists(closeList, bottomX, bottomY)) {
arrayList.add(new Node(bottomX, bottomY));
}
int leftX = currentNode.x - 1;
int leftY = currentNode.y;
if (canReach(leftX, leftY) && !exists(closeList, leftX, leftY)) {
arrayList.add(new Node(leftX, leftY));
}
int rightX = currentNode.x + 1;
int rightY = currentNode.y;
if (canReach(rightX, rightY) && !exists(closeList, rightX, rightY)) {
arrayList.add(new Node(rightX, rightY));
}
return arrayList;
}
//判断此处坐标是否可达,若超界或者是墙则不可达
public boolean canReach(int x, int y) {
if (x >=0 && x < NODES.length && y >=0 && y < NODES.length && NODES[x][y]==1) {
return true;
}
return false;
}
//A*寻路过程
public Node findPath(Node startNode, Node endNode) {
openList.add(startNode);// 把起点加入 open list
while (openList.size() > 0) {
Node currentNode = findMinFNodeInOpenList();// 遍历 open list ,查找 F值最小的节点,把它作为当前要处理的节点
openList.remove(currentNode);// 从open list中移除
closeList.add(currentNode);// 把这个节点移到 close list
ArrayList<Node> neighborNodes = findNeighborNodes(currentNode);
for (Node node : neighborNodes) {//遍历四个邻居
if (exists(openList, node)) {
foundPoint(currentNode, node);
} else {
notFoundPoint(currentNode, endNode, node);
}
}
if (find(openList, endNode) != null) {
return find(openList, endNode);//找到终点了并返回
}
}
return find(openList, endNode);
}
//如果它已经在 open list 中,检查通过当前方格到达该方格是否代价更小,即G值更小。如果是这样,把它的父亲设置为当前方格,并重新计算它的 G 和 F 值。
private void foundPoint(Node tempStart, Node node) {
int G = calcG(tempStart, node);
if (G < node.G) {
node.parent = tempStart;
node.G = G;
node.calcF();
}
}
//如果它不在 open list 中,把它加入 open list ,并且把当前方格设置为它的父亲,计算该方格的 F , G 和 H 值。
private void notFoundPoint(Node tempStart, Node end, Node node) {
node.parent = tempStart;
node.G = calcG(tempStart, node);
node.H = calcH(end, node);
node.calcF();
openList.add(node);
}
//计算G值的方法
private int calcG(Node start, Node node) {
int G = STEP;
int parentG = node.parent != null ? node.parent.G : 0;
return G + parentG;
}
//计算H值的方法
private int calcH(Node end, Node node) {
int step = Math.abs(node.x - end.x) + Math.abs(node.y - end.y);
return step * STEP;
}
//找到终点的方法
public static Node find(List<Node> nodes, Node point) {
for (Node n : nodes)
if ((n.x == point.x) && (n.y == point.y)) {
return n;
}
return null;
}
//下面两个是exist方法的重载,判断不同参数情况时节点是否在列表里
public static boolean exists(List<Node> nodes, Node node) {
for (Node n : nodes) {
if ((n.x == node.x) && (n.y == node.y)) {
return true;
}
}
return false;
}
public static boolean exists(List<Node> nodes, int x, int y) {
for (Node n : nodes) {
if ((n.x == x) && (n.y == y)) {
return true;
}
}
return false;
}
//节点类,定义了每一个节点的属性
public static class Node {
public Node(int x, int y) {
this.x = x;
this.y = y;
}
public int x;
public int y;
public int F;
public int G;
public int H;
public void calcF() {
this.F = this.G + this.H;
}
public Node parent;
}
}
Main类
package com.company;
import javax.swing.*;
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame(); //生成窗口
frame.setSize(770,825); //设置窗口大小
Panel maze = new Panel();
frame.add(maze); //添加游戏面板
frame.setLocationRelativeTo(null);//显示屏幕中央
frame.setResizable(false);//固定屏幕
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//关闭指令
frame.setVisible(true); //窗口可视化
}
}
Maze类
package com.company;
import java.util.Random;
class Maze {
// 初始化一个地图,产生的二维数组大小实际为(2width+1) * (2height+1)
private static int width;
private static int height;
public static int[][] map;// 存放迷宫的数组
private static int r;
private static int c;
Maze(int r0, int c0) {
width = r0;
height = c0;
r = 2 * width + 1;
c = 2 * height + 1;
map = new int[r][c];
}
public static int[][] Init() {
for (int i = 0; i < r; i++) // 将所有格子都设为墙
for (int j = 0; j < c; j++)
map[i][j] = 0;// 0 为墙 1为路
// 中间格子放为1
for (int i = 0; i < width; i++)
for (int j = 0; j < height; j++)
map[2 * i + 1][2 * j + 1] = 1;// 0 为墙 1为路
rdPrime();
return map;
}
public static void rdPrime() {
// ok存放已访问队列,not存放没有访问队列
int[] ok, not;
int sum = width * height;
int count = 0;// 记录访问过点的数量
ok = new int[sum];
not = new int[sum];
// 0左 1右 3上 2下
int[] offR = {-1, 1, 0, 0};
int[] offC = {0, 0, 1, -1};
// 四个方向的偏移 左右上下
int[] offS = {-1, 1, width, -width}; // 向上向下移动都是变化一行
// 初始化 ok中0代表未访问,not中0代表未访问
for (int i = 0; i < sum; i++) {
ok[i] =0;
not[i] = 0;
}
// 起点
Random rd = new Random();
ok[0] = rd.nextInt(sum);// 起始点
int pos = ok[0];
// 第一个点存入
not[pos] = 1;//设为已访问
while (count < sum) {
// 取出现在的点
int x = pos % width;
int y = pos / width;// 该点的坐标
int offpos = -1;
int m = 0;
while (++m < 4) {
// 随机访问最近的点
int point = rd.nextInt(4); // 0-3
int repos;
int movex, movey;
// 计算出移动方位
repos = pos + offS[point];// 移动后的下标
movex = x + offR[point];// 移动后的方位
movey = y + offC[point];
if (movey >= 0 && movex >= 0 && movex < width && movey < height && repos >= 0 && repos < sum
&& not[repos] != 1) {
not[repos] = 1;// 把该点标记为已访问
ok[++count] = repos;//
pos = repos;// 把该点作为起点
offpos = point;
// 打通墙壁
map[2 * x + 1 + offR[point]][2 * y + 1 + offC[point]] = 1;
break;//回到while (count < sum)
} else {
if (count == sum - 1)
return;
}
}
if (offpos < 0) {//初始值为-1,如果上下左右有未访问的路则大于0,小于0则说明上下左右都访问过了。
pos = ok[rd.nextInt(count + 1)];
}
}
}
}
Panel类
package com.company;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class Panel extends JPanel implements KeyListener,ActionListener {
public final static int WALL = 0;//障碍物
public final static int ROAD = 1;//通路
public static int width; //地图规模
public static int height;
public final static int current = 2; //标识当前位置图例
public final static int MAXSTEPS = 200; //游戏的最大步数
static int mx = 1; //当前位置(curX,curY)
static int my = 1;
static int score = 0; //游戏得分及步数
static int steps = 0; // 步数
static int level = 3;//难度
static int[][] map; //储存迷宫
private JButton ans = new JButton("显示路径"); //按键和键盘方式,未实现,直接全用键盘操作了
private JButton remake = new JButton("重置迷宫");
private JPanel jp = new JPanel();
private JButton hide = new JButton("隐藏路径");
private JButton exit = new JButton("退出游戏");
private JButton start = new JButton("开始游戏");
public static int Difficult(int level) { //游戏难度,初始为10,难度+1,地图+2
int number = 10;
for (int i = 0; i < level; i++) {
number += 2;
}
return number;
}
public static void MapRandom() { //随机迷宫
width = Difficult(level);
height = Difficult(level);
Maze maze = new Maze(width, height); //初始化迷宫
map = Maze.Init(); //继承迷宫
map[1][1] = 2; //设置迷宫的入口及出口
mx = 1;
my = 1;
//map[map.length - 2][map.length - 2] = 2;
}
//游戏界面初始化和设计
public void paintComponent(Graphics g) {
super.paintComponent(g);
this.addKeyListener(this);
//count = Difficult(level);
g.setColor(new Color(0,139,139));//添加RGB背景色
g.fillRect(0, 0, 760, 807);
//Date.location.paintIcon(this,g,0,50);
//g.setColor(new Color(224, 238, 238));
for (int i = 0; i < map.length; i++) { //填充颜色的方法画出迷宫的wall和load
for (int j = 0; j < map.length; j++) {
if(i==map.length-2&&j==map.length-2){
g.setColor(Color.RED);
g.fillRect(10 * i + 10, 10 * j + 50, 10, 10);
}else if (map[i][j] == 0) {
g.setColor(Color.BLACK);
g.fillRect(10 * i + 10, 10 * j + 50, 10, 10);
} else if (map[i][j] == 1) {
g.setColor(Color.GRAY);
g.fillRect(10 * i + 10, 10 * j + 50, 10, 10);
} else if (map[i][j] == 2) {
g.setColor(Color.RED);
g.fillRect(10 * i + 10, 10 * j + 50, 10, 10);
}
}
g.setColor(new Color(188, 250, 250));
g.fillRect(0, 0, 1000, 40);
g.setColor(Color.BLACK);
g.setFont(new Font("仿宋", Font.BOLD, 20));
g.drawString("重置游戏(X)", 10, 25);
g.drawString("寻路(Y)", 150, 25);
g.drawString("当前步数:" + steps, 250, 25);
g.drawString("难度:"+level,400,25);
g.drawString("W:难度+ S:难度-",500,25);
if (Panel.isSuccess()) {
g.setFont(new Font("宋体", Font.BOLD, 50));
g.drawString("恭喜通关", 500, 400);
}
}
}
//将A*算法得出的路径返回给map
public void ans() {
AStar aStart = new AStar(map);
map = AStar.ans();
}
public Panel() {
MapRandom();
this.setFocusable(true);
this.addKeyListener(this);
}
//判断是否走出迷宫
public static boolean isSuccess() {
//score += MazePanel.MAXSTEPS - steps;
return map.length - 2 == mx && map.length - 2 == my;
}
@Override
public void actionPerformed(ActionEvent e) {
}
static int i=0;
@Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
int x = mx, y = my;
//游戏移动方式和限制,下一步不能为墙且单次监听,不加i=0会产生bug
if(!isSuccess()&&steps<MAXSTEPS) {
if (key == KeyEvent.VK_LEFT && map[x - 1][y] != Panel.WALL&&i==0) {//左移
map[x - 1][y] = Panel.current; //更改当前位置到移动到的位置
map[x][y] = 1;//恢复地图原有的图标
mx--;i++; //更改当前位置坐标
steps++;
} else if (key == KeyEvent.VK_RIGHT&&map[x + 1][y] != Panel.WALL&&i==0) {
map[x + 1][y] = Panel.current; //右移
map[x][y] = 1;
mx++;i++;
steps++;
} else if (key == KeyEvent.VK_UP&&map[x][y - 1] != Panel.WALL&&i==0) {
map[x][y - 1] = Panel.current; //上移
map[x][y] = 1;
my--;i++;
steps++;
} else if (key == KeyEvent.VK_DOWN&&map[x][y + 1] != Panel.WALL&&i==0) {
map[x][y + 1] = 2; //下移
map[x][y] = 1;
my++;i++;
steps++;
}else if(key == KeyEvent.VK_Y&&i==0){ //显示路径
ans();
}else if(key == KeyEvent.VK_X&&i==0){ //重置迷宫
MapRandom();
}
else if(key==KeyEvent.VK_W&&i==0&&level<13){ //增加迷宫难度
level++;i++;
MapRandom();
}else if(key==KeyEvent.VK_S&&i==0&&level>=3){ //减少迷宫难度
level--;i++;
MapRandom();
}
}
repaint(); //刷新游戏界面
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
i=0; //键盘监听限制,不加限制会一次移动好几格
}
}