目录
一、简单计算器的实现
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Arrays;
import java.util.Objects;
import java.util.Stack;
import javax.swing.*;
//Calculator类,继承JFrame框架,实现事件监听器接口
class Calculator extends JFrame implements ActionListener {
private final String[] KEYS = {"7", "8", "9", "AC",
"4", "5", "6", "-",
"1", "2", "3", "+",
"0", "/", "x*x", "*",
"(", ")", ".", "="};
private JButton keys[] = new JButton[KEYS.length];
private JTextArea resultText = new JTextArea(); // 文本域组件TextArea可容纳多行文本;文本框内容初始值设为0
private JTextArea HistoryText = new JTextArea(); // 历史记录文本框初始值设为空
private JPanel jp2 = new JPanel();
private JScrollPane gdt1 = new JScrollPane(resultText); //给输入显示屏文本域新建一个垂直滚动滑条
private JScrollPane gdt2 = new JScrollPane(HistoryText); //给历史记录文本域新建一个垂直滚动滑条
private JLabel label = new JLabel("历史记录");
private String input = ""; //计算文本框输入的中缀表达式
// 构造方法
public Calculator() {
resultText.setBounds(20, 18, 255, 115); // 设置文本框大小
resultText.setAlignmentX(LEFT_ALIGNMENT); // 文本框内容左对齐
resultText.setEditable(false); // 文本框不允许修改结果
resultText.setFont(new Font("font", Font.PLAIN, 18)); //设置结果文本框输入文字的字体、类型、大小
HistoryText.setFont(new Font("font", Font.PLAIN, 18)); //设置历史记录文本框输入文字的字体、类型、大小
HistoryText.setBounds(290, 40, 250, 330); // 设置文本框大小
HistoryText.setAlignmentX(LEFT_ALIGNMENT); // 文本框内容左对齐
HistoryText.setEditable(false); // 文本框不允许修改结果
label.setBounds(300, 15, 100, 20); //设置标签位置及大小
jp2.setBounds(290, 40, 250, 330); //设置面板窗口位置及大小
jp2.setLayout(new GridLayout());
JPanel jp1 = new JPanel();
jp1.setBounds(20, 18, 255, 115); //设置面板窗口位置及大小
jp1.setLayout(new GridLayout());
resultText.setLineWrap(true); // 激活自动换行功能
resultText.setWrapStyleWord(true); // 激活断行不断字功能
HistoryText.setLineWrap(true); //自动换行
HistoryText.setWrapStyleWord(true);
gdt1.setViewportView(resultText); //使滚动条显示出来
gdt2.setViewportView(HistoryText);
gdt1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); //设置让垂直滚动条一直显示
gdt2.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); //设置让垂直滚动条一直显示
jp1.add(gdt1); //将滚动条添加入面板窗口中
jp2.add(gdt2);
this.add(jp1); //将面板添加到总窗体中
this.add(jp2); //将面板添加到总窗体中
this.setLayout(null);
this.add(label); // 新建“历史记录”标签
// 放置按钮 x,y为按钮的横纵坐标
int x = 20, y = 150;
for (int i = 0; i < KEYS.length; i++) {
keys[i] = new JButton();
keys[i].setText(KEYS[i]);
keys[i].setBounds(x, y, 60, 40);
if (x < 215) {
x += 65;
} else {
x = 20;
y += 45;
}
this.add(keys[i]);
}
for (int i = 0; i < KEYS.length; i++) // 每个按钮都注册事件监听器
{
keys[i].addActionListener(this);
}
this.setResizable(false);//不允许调整计算器界面大小
this.setBounds(700, 300, 560, 450);//计算器界面位置和大小
this.setDefaultCloseOperation(EXIT_ON_CLOSE);//关闭计算器程序终止
this.setVisible(true);
}
// 事件处理
public void actionPerformed(ActionEvent e) {
String label = e.getActionCommand(); //获得事件源的标签
if (Objects.equals(label, "AC")) //清空按钮,消除显示屏文本框前面所有的输入和结果
{
input = "";
resultText.setText(""); //更新文本域的显示
} else if (Objects.equals(label, "x*x")) {
String m;
if(input.isEmpty()) m="error!";
else m = String.valueOf(pfys(input));
resultText.setText(input + "^2" + "=" + m);
HistoryText.setText(HistoryText.getText() + resultText.getText() + "\n");
input = m;
} else if (Objects.equals(label, "=")) {
if (input.isEmpty()) return;
String[] s = houzhui(input); //将中缀表达式转换为后缀表达式
double result = Result(s); //计算后缀表达式得出最终算式结果
resultText.setText(input + "=" + result);
HistoryText.setText(HistoryText.getText() + resultText.getText() + "\n");
} else {
input = input + label;
resultText.setText(input);
}
}
//将中缀表达式转换为后缀表达式
private String[] houzhui(String infix) { //infix 中缀
String s = "";// 用于承接多位数的字符串
Stack<String> opStack = new Stack<String>(); // 操作符静态栈,对用户输入的操作符进行处理,用于存储运算符
Stack<String> postQueue = new Stack<String>(); // 后缀表达式,为了将多位数存储为独立的字符串
System.out.println("中缀:" + infix);
for (int i = 0; i < infix.length(); i++) // 遍历中缀表达式
// indexof函数,返回字串首次出现的位置;charAt函数返回index位置处的字符;
{
if ("1234567890.".indexOf(infix.charAt(i)) >= 0) { // 遇到数字字符直接入队
//判断并记录多位操作数,比如,中缀表达式:234+4*2,我们扫描这个字符串的时候,s的作用相当于用来存储长度为3个字符的操作数:234
s = "";// 作为承接字符,每次开始时都要清空
for (; i < infix.length() && "0123456789.".indexOf(infix.charAt(i)) >= 0; i++) {
s = s + infix.charAt(i);
}
i--; //避免跳过对非数字字符的处理
postQueue.push(s); // 数字字符直接加入后缀表达式
} else if ("(".indexOf(infix.charAt(i)) >= 0) { // 遇到左括号
opStack.push(String.valueOf(infix.charAt(i))); // 左括号入栈
} else if (")".indexOf(infix.charAt(i)) >= 0) { // 遇到右括号
while (!opStack.peek().equals("(")) { // 栈顶元素循环出栈,直到遇到左括号为止
postQueue.push(opStack.pop());
}
opStack.pop(); //删除左括号
} else if ("*%/+-".indexOf(infix.charAt(i)) >= 0) // 遇到运算符
{
if (opStack.empty() || "(".contains(opStack.peek())) { // 若栈为空或栈顶元素为左括号则直接入栈
opStack.push(String.valueOf(infix.charAt(i)));
} else {
// 当栈顶元素为高优先级或同级运算符时,让栈顶元素出栈进入后缀表达式后,直到符合规则后,当前运算符再入栈
boolean rule = ("*%/+-".contains(opStack.peek()) && "+-".indexOf(infix.charAt(i)) >= 0) || ("*%/".contains(opStack.peek()) && "*%/".indexOf(infix.charAt(i)) >= 0);
while (!opStack.empty() && rule) {
postQueue.push(opStack.peek()); //peek()方法:返回栈顶的元素但不移除它
opStack.pop();
}
opStack.push(String.valueOf(infix.charAt(i))); // 当前元素入栈
}
}
}
while (!opStack.empty()) {// 遍历结束后将栈中剩余元素依次出栈进入后缀表达式
postQueue.push(opStack.pop());
}
//将后缀表达式栈转换为字符串数组格式
String[] suffix = new String[postQueue.size()];
for (int i = postQueue.size() - 1; i >= 0; i--) {
suffix[i] = postQueue.pop();
}
System.out.println("后缀:" + Arrays.toString(suffix.clone()));
return suffix;
}
//平方运算方法
public double pfys(String str) {
double a = Double.parseDouble(str);
return Math.pow(a, 2);
}
// 计算后缀表达式,并返回最终结果
public double Result(String[] suffix) { //suffix 后缀
Stack<String> Result = new Stack<>();// 顺序存储的栈,数据类型为字符串
int i;
for (i = 0; i < suffix.length; i++) {
if ("1234567890.".indexOf(suffix[i].charAt(0)) >= 0) { //遇到数字,直接入栈
Result.push(suffix[i]);
} else { // 遇到运算符字符,将栈顶两个元素出栈计算并将结果返回栈顶
double x, y, n = 0;
x = Double.parseDouble(Result.pop()); // 顺序出栈两个数字字符串,并转换为double类型
y = Double.parseDouble(Result.pop());
switch (suffix[i]) {
case "*":
n = y * x;
break;
case "/":
if (x == 0) return 0;
else n = y / x;
break;
case "-":
n = y - x;
break;
case "+":
n = y + x;
break;
}
Result.push(String.valueOf(n)); // 将运算结果重新入栈
}
}
System.out.println("return:" + Result.peek());
return Double.parseDouble(Result.peek()); // 返回最终结果
}
// 主函数
public static void main(String[] args) {
Calculator a = new Calculator();
}
}
二、贪吃蛇游戏的设计与开发
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Objects;
import java.util.Random;
public class GamePanel extends JPanel implements KeyListener , ActionListener {
private final JButton jba = new JButton("开始");
private final JButton jbb = new JButton("暂停");
private final JButton jbc = new JButton("停止退出");
//定义蛇的数据结构
int length;//蛇的长度
int snakex []=new int[600];//蛇的x坐标 25*25
int snakey []=new int[500];//蛇的y坐标 25*25
String fx;
//食物的坐标
int foodx;
int foody;
Random random=new Random();
int score;//成绩
//游戏当前的状态:开始 停止
boolean isStart=false;//默认是停止
boolean isFail=false;//默认游戏状态失败
//定时器 以毫秒为单位 1000ms=1s
Timer timer=new Timer(200,this);//100毫秒刷新(执行)一次
//构造器
public GamePanel() {
init();
//获得焦点和键盘事件
this.setFocusable(true);//获得焦点事件
this.addKeyListener(this);//获得键盘监听事件
timer.start();//游戏一开始定时器就启动
}
//初始化方法
public void init(){
length=3;//小蛇的长度
snakex[0]=100;snakey[0]=100;//蛇的脑袋的坐标
snakex[1]=75;snakey[1]=100;//蛇的第一个身体的坐标
snakex[2]=50;snakey[2]=100;//蛇的第二个身体的坐标
fx="R";//初始化方向向右
//把食物随机放在界面上
foodx=25+25*random.nextInt(34);
foody=75+25*random.nextInt(24);
score=0;
}
//绘制面板,我们游戏中的所有东西都是用这个画笔来画
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);//清屏
this.setBackground(Color.WHITE);
Data.header.paintIcon(this,g,155,11);//顶部标题写上去
g.fillRect(25,75,850,600);//默认的游戏界面
//添加按钮
this.add(jba);
this.add(jbb);
this.add(jbc);
this.setVisible(true);
jba.setBounds(50,15,60,33);
jbb.setBounds(120,15,60,33);
jbc.setBounds(190,15,120,33);
jba.addActionListener(this);
jbb.addActionListener(this);
jbc.addActionListener(this);
//画积分
g.setColor(Color.black);
g.setFont(new Font("微软雅黑",Font.BOLD,18));
g.drawString("长度:"+length,650,30);
g.drawString("积分:"+score,650,55);
//画食物
Data.food.paintIcon(this,g,foodx,foody);
//把小蛇画上去
if(fx.equals("R")) {
Data.right.paintIcon(this, g, snakex[0], snakey[0]);//蛇头初始化向右,需要通过方向来判断
}else if(fx.equals("L")){
Data.left.paintIcon(this, g, snakex[0], snakey[0]);//蛇头初始化向左,需要通过方向来判断
}else if(fx.equals("U")){
Data.up.paintIcon(this, g, snakex[0], snakey[0]);//蛇头初始化向上,需要通过方向来判断
}else if(fx.equals("D")){
Data.down.paintIcon(this, g, snakex[0], snakey[0]);//蛇头初始化向下,需要通过方向来判断
}
//从第一节身体开始变化所以i=1
for (int i = 1; i <length ; i++) {
Data.body.paintIcon(this,g,snakex[i],snakey[i]);
}
//游戏状态
if(!isStart){
g.setColor(Color.white);
//设置字体
g.setFont(new Font("微软雅黑",Font.BOLD,40));
g.drawString("按下空格开始游戏",300,300);
}
if(isFail){
g.setColor(Color.RED);
//设置字体
g.setFont(new Font("微软雅黑",Font.BOLD,40));
g.drawString("游戏结束!",365,300);
}
}
//键盘监听事件
@Override
public void keyPressed(KeyEvent e) {
int keycode=e.getKeyCode();//获得键盘的按键是哪一个
if(keycode==KeyEvent.VK_SPACE){//如果按下的键是空格键
if(isFail){
//重新开始
isFail=false;
init();
}else {
isStart = !isStart;//取反
}
repaint();
}
//小蛇移动
if(keycode==KeyEvent.VK_UP){//上
fx="U";
}else if(keycode==KeyEvent.VK_DOWN){//下
fx="D";
}else if(keycode==KeyEvent.VK_LEFT){//左
fx="L";
}else if(keycode==KeyEvent.VK_RIGHT){//右
fx="R";
}
}
//事件监听,需要通过固定的时间(事件)来刷新
@Override
public void actionPerformed(ActionEvent e) {
if(isStart && !isFail){//如果游戏是开始状态,就让小蛇动起来
//吃食物
if(snakex[0]==foodx&&snakey[0]==foody){
length++;//长度加1
//分数加10
score=score+10;
//再次随机生成食物
foodx=25+25*random.nextInt(34);
foody=75+25*random.nextInt(24);
}
//移动
for (int i = length-1; i >0 ; i--) {//后一节移动到前一节的位置
snakex[i]=snakex[i-1];//向前移动一节
snakey[i]=snakey[i-1];
}
//走向
if(fx.equals("R")){
snakex[0]=snakex[0]+25;
/*if(snakex[0]>825){//边界判断
fx="D";
}else */if(snakex[0]>850){
isFail = true;//如果小蛇飞出界面,则游戏失败
for (int i = 0; i <length ; i++) {
snakex[i]=snakex[i]-25;
}
}
}else if(fx.equals("L")){
snakex[0]=snakex[0]-25;
/*if(snakex[0]<50){
fx="U";
}
else*/ if(snakex[0]<25){//边界判断
isFail = true;
for (int i = 0; i <length ; i++) {
snakex[i]=snakex[i]+25;
}
}
}else if(fx.equals("D")){
snakey[0]=snakey[0]+25;
/*if(snakey[0]>625){//边界判断
fx="L";
}else */if(snakey[0]>650){
isFail = true;
for (int i = 0; i <length ; i++) {
snakey[i]=snakey[i]+25;
}
}
}else if(fx.equals("U")){
snakey[0]=snakey[0]-25;
/*if(snakey[0]<100){//边界判断
fx="R";
}else*/ if(snakey[0]<75){
isFail = true;
for (int i = 0; i <length ; i++) {
snakey[i]=snakey[i]-25;
}
}
}
//失败判定:撞到自己就算失败
for (int i = 1; i <length ; i++) {
if(snakex[0]==snakex[i]&&snakey[0]==snakey[i]){
isFail=true;
}
}
repaint();//重画界面
}
timer.start();//定时器开启
String label = e.getActionCommand(); //获得事件源的标签
if (Objects.equals(label, "开始"))
{
isFail=false;
init();
isStart =true;
repaint();
} else if (Objects.equals(label, "暂停")) {
if(isStart){
isStart= false;
}else if(isStart=false){
isStart=true;
repaint();
}
} else if(Objects.equals(label, "停止退出")) {
isFail=true;
System.exit(0);
}
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void keyTyped(KeyEvent e) {
}
}
import javax.swing.*;
import java.net.URL;
//数据中心
public class Data {
public static URL headerURL=Data.class.getResource("");//顶部
public static ImageIcon header=new ImageIcon(headerURL);
public static URL upURL=Data.class.getResource("/img/shang.png");//上
public static ImageIcon up=new ImageIcon(upURL);
public static URL downURL=Data.class.getResource("/img/xia.png");//下
public static ImageIcon down=new ImageIcon(downURL);
public static URL leftURL=Data.class.getResource("/img/zuo.png");//左
public static ImageIcon left=new ImageIcon(leftURL);
public static URL rightURL=Data.class.getResource("/img/you.png");//右
public static ImageIcon right=new ImageIcon(rightURL);
public static URL bodyURL=Data.class.getResource("/img/body.png");//身体
public static ImageIcon body=new ImageIcon(bodyURL);
public static URL foodURL=Data.class.getResource("/img/food.png");//食物
public static ImageIcon food=new ImageIcon(foodURL);
}
import javax.swing.*;
//游戏主启动类
public class SnakeGame {
public static void main(String[] args) {
JFrame frame=new JFrame();
frame.setBounds(500,150,900,720);
frame.setResizable(false);//窗口大小不可变
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);//退出为系统退出
frame.add(new GamePanel());
frame.setVisible(true);
}
}
蛇身: 食物:
蛇头:
三、基于A*搜索算法迷宫游戏开发
import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Stack;
class Node {//计算估价函数
public int x,y;
public Node parent;
public Node(int x, int y) {
this.x = x;
this.y = y;
}
public int F;//F为估价函数
public int G;//G代表的是从初始位置Start沿着已生成的路径到指定待检测结点移动开销
public int H;//H表示待检测结点到目标节点B的估计移动开销
public void calcF() {
this.F = this.G + this.H;
}
}
class Maze extends JPanel implements KeyListener {
//生成地图用到变量
final static int wall =0; //代表墙
final static int road =1; //代表空地
static int num = 21; //迷宫长度
int width = 20; //迷宫宽度
static int [][] mMap; //迷宫
boolean[][] visit; //用来标记某一格是否被访问过
Node start = new Node(1,1); //开始节点
Node end = new Node(num-2,num-2);//结束节点
Node cur; //当前格
Node next; //下一格
Stack<Node> path = new Stack<>();//记录生成地图时遍历的顺序
//走迷宫时用到的变量
private Node movePerson;
List<Integer> xPath=new ArrayList<>();//记录迷宫中行进的轨迹
List<Integer> yPath=new ArrayList<>();
private boolean drawPath = false;
//A*算法使用到的变量
public int sValue = 10;//设每一步的权值为10
private ArrayList<Node> openList = new ArrayList<>();//维护一个开放列表
private ArrayList<Node> closeList = new ArrayList<>();//维护一个关闭列表
//初始化,初始化迷宫参数
Maze(){
mMap = new int [num][num];
visit = new boolean[num][num];
for (int i = 0; i < num; i = i+2) {//初始化地图的空格
for (int j = 0; j < num; j=j+2){
mMap[i][j] = wall;//其余均为墙
visit[i][j] = false;
}
}
for (int i = 1; i < num; i = i+2) {//初始化地图的空格
for (int j = 1; j < num; j=j+2){
mMap[i][j] = road;//奇数行奇数列的格子均为路
visit[i][j] = false;
}
}
visit[start.x][start.y] = true;
mMap[start.x][start.y] = road;
cur = start; //将当前格标记为开始格
movePerson=new Node(start.x-1,start.y);
drawPath=false;
createMaze();
this.addKeyListener(this);
this.setFocusable(true);
}
public void init(){//第一轮结束后,再次初始化地图
mMap = new int [num][num];
visit = new boolean[num][num];
for (int i = 0; i < num; i = i+2) {//初始化地图的空格
for (int j = 0; j < num; j=j+2){
mMap[i][j] = wall;//其余均为墙
visit[i][j] = false;
}
}
for (int i = 1; i < num; i = i+2) {//初始化地图的空格
for (int j = 1; j < num; j=j+2){
mMap[i][j] = road;//奇数行奇数列的格子均为路
visit[i][j] = false;
}
}
visit[start.x][start.y] = true;
mMap[start.x][start.y] = road;
cur = start; //将当前格标记为开始格
movePerson=new Node(start.x-1,start.y);//设置移动的起始点
drawPath=false;
xPath.clear();//清空行走的路径坐标
yPath.clear();
openList.clear();
closeList.clear();
createMaze();
this.setFocusable(true);
repaint();
}
//深度优先遍历
void createMaze() {
path.push(cur); //将当前格压入栈
while(!path.empty()) {
ArrayList<Node> mNei=notVisitedNei(cur);
if(mNei.size()==0){//如果该格子没有可访问的邻接格,则跳回上一个格子
cur = path.pop();
continue;
}
next = mNei.get(new Random().nextInt(mNei.size()));//随机选取一个邻接格
int x = next.x;
int y = next.y;
if(visit[x][y]){//如果该节点被访问过,则回到上一步继续寻找
cur = path.pop();
}
else{//否则将当前格压入栈,标记当前格为已访问,并且在迷宫地图上移除障碍物
path.push(next);
visit[x][y] = true;
mMap[x][y] = road;
mMap[(cur.x + x) / 2][(cur.y + y) / 2] = road; //打通当前格与下一格
cur = next;//当前格等于下一格
}
}
mMap[start.x-1][start.y]=1;//设置入口
mMap[end.x+1][end.y]=1;//设置出口
}
public ArrayList<Node> notVisitedNei(Node node)//寻找未访问的邻接节点
{
int []nei={2,0,-2,0,2};
ArrayList<Node> list = new ArrayList<Node>();
for(int i = 0; i < nei.length-1; i++)
{
int x = node.x + nei[i];
int y = node.y + nei[i+1];
if( x >= 0 && x < num && y >= 0 && y < num)
{
if(!visit[x][y])//未访问,则入数组
list.add(new Node(x,y));
}
}
return list;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
this.setBackground(Color.WHITE);
g.setColor(Color.lightGray);//画墙
for(int i=0;i<num;i++){
for(int j=0;j<num;j++){
if(mMap[i][j]==0){
g.fillRect(10+i*width,10+j*width,width,width);
}
}
}
if(drawPath){//画出A*算法求得的路径
g.setColor(Color.orange);
Node parent = findPath(start, end); //父节点
ArrayList<Node> arrayList = new ArrayList<Node>();
while (parent != null) {
arrayList.add(new Node(parent.x, parent.y));
parent = parent.parent;
}
for (int i = 0; i <num; i++) {
for (int j = 0; j < num; j++) {
if (exists(arrayList, i, j)) {
g.fillOval(10+i*width+width/4 , 10+j*width+width/4,
width / 2, width / 2);
}
}
}
}
g.setColor(Color.gray);//画移动的轨迹
for(int i=0;i<xPath.size();i++){
g.fillOval(10+xPath.get(i)*width+width/4 , 10+yPath.get(i)*width+width/4,
width / 2, width / 2);
}
g.setColor(Color.black);//画点的移动
g.fillOval(10+movePerson.x*width+width/4 , 10+movePerson.y*width+width/4,
width / 2, width / 2);
}
private boolean isOutOfBorder(int x, int y) {//越界检测
if (x > num-1 || y >num-1 || x < 0 || y < 0) {
return true;
}
return false;
}
private void checkWin() {//通关检测
if (movePerson.x==num-1 && movePerson.y==num-2) {
JOptionPane.showMessageDialog(null, "闯关成功! " +"\n", "My Maze",
JOptionPane.PLAIN_MESSAGE);
init();
}
}
//A*算法
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 (!isOutOfBorder(topX, topY) && !exists(closeList, topX, topY) && mMap[topX][topY]==1) {
arrayList.add(new Node(topX, topY));
}
int bottomX = currentNode.x;
int bottomY = currentNode.y + 1;
if (!isOutOfBorder(bottomX, bottomY) && !exists(closeList, bottomX, bottomY) && mMap[bottomX][bottomY]==1) {
arrayList.add(new Node(bottomX, bottomY));
}
int leftX = currentNode.x - 1;
int leftY = currentNode.y;
if (!isOutOfBorder(leftX, leftY) && !exists(closeList, leftX, leftY) && mMap[leftX][leftY]==1) {
arrayList.add(new Node(leftX, leftY));
}
int rightX = currentNode.x + 1;
int rightY = currentNode.y;
if (!isOutOfBorder(rightX, rightY) && !exists(closeList, rightX, rightY) && mMap[rightX][rightY]==1) {
arrayList.add(new Node(rightX, rightY));
}
return arrayList;
}
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)) {//如果邻居节点在open列表中
foundPoint(currentNode, node);//更新列表中父节点和估价函数信息
} else {
notFoundPoint(currentNode, endNode, node);//如果邻居节点不在open列表中,则将该点加入open列表中
}
}
if (find(openList, endNode) != null) {//如果找到尾节点,则返回尾节点
return find(openList, endNode);
}
}
return null;
}
private void foundPoint(Node tempStart, Node node) {
int G = calcG(tempStart, node);
if (G < node.G) {
node.parent = tempStart;
node.G = G;
node.calcF();
}
}
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);
}
private int calcG(Node start, Node node) {
int G = sValue;
int parentG = node.parent != null ? node.parent.G : 0;
return G + parentG;
}
private int calcH(Node end, Node node) {
int step = Math.abs(node.x - end.x) + Math.abs(node.y - end.y);
return step * sValue;
}
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;
}
public static boolean exists(List<Node> nodes, Node node) {//判断节点是否存在某一list中
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) {//判断节点是否存在某一list中
for (Node n : nodes) {
if ((n.x == x) && (n.y == y)) {
return true;
}
}
return false;
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {//响应键盘
int keyCode = e.getKeyCode();
int x=movePerson.x;
int y=movePerson.y;
switch (keyCode){
case KeyEvent.VK_SPACE: //空格键选择是否路径的显示的情况
if (drawPath) {
drawPath = false;
} else {
drawPath = true;
}
repaint();
break;
case KeyEvent.VK_LEFT: //根据键盘控制移动
x--;
break;
case KeyEvent.VK_RIGHT:
x++;
break;
case KeyEvent.VK_UP:
y--;
break;
case KeyEvent.VK_DOWN:
y++;
break;
case KeyEvent.VK_F5:
x=0;y=0;
init();
}
if(!isOutOfBorder(x,y)&&mMap[x][y]==1){
xPath.add(movePerson.x);
yPath.add(movePerson.y);
movePerson.x=x;
movePerson.y=y;
}
repaint();
checkWin();
}
@Override
public void keyReleased(KeyEvent e) {
}
public static void main(String[] args) {
JPanel p = new Maze();
JFrame frame = new JFrame("MY MAZE(按空格键显示或隐藏路径提示)");
frame.getContentPane().add(p);//添加画布
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//使用 System exit 方法退出应用程序。0
frame.setSize(460, 480);//设置窗口大小
frame.setLocation(200, 200);
frame.setVisible(true);
}
}