计算机软件技术实习 源代码

目录

一、简单计算器的实现

二、贪吃蛇游戏的设计与开发

三、基于A*搜索算法迷宫游戏开发


一、简单计算器的实现

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);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值