当前位置: 移动技术网 > IT编程>开发语言>Java > Java编写迷宫小游戏

Java编写迷宫小游戏

2019年07月22日  | 移动技术网IT编程  | 我要评论
缘起:   去年(大三上学期)比较喜欢写小游戏,于是想试着写个迷宫试一下。 程序效果: 按下空格显示路径: 思考过程:   迷宫由一个一个格子组成,要

缘起:

  去年(大三上学期)比较喜欢写小游戏,于是想试着写个迷宫试一下。

程序效果:

按下空格显示路径:

思考过程:

  迷宫由一个一个格子组成,要求从入口到出口只有一条路径.

  想了一下各种数据结构,似乎树是比较合适的,从根节点到每一个子节点都只有一条路径。假设入口是根节点,出口是树中某个子节点,那么,从根节点到该子节点的路径肯定是唯一的。

  所以如果能构造一棵树把所有的格子都覆盖到,也就能够做出一个迷宫了。

  另外还要求树的父节点和子节点必须是界面上相邻的格子。

  在界面显示时,父节点和子节点之间共用的边不画,其他的边都画出来,就能画出一个迷宫。

  之后就是想一下该怎么实现这样一棵树。

  首要的两个问题:

    1、树怎么表示?

    2、怎么构造这棵树?

1.树怎么表示?

  假设像写二叉树一样实现这棵树,那么每个树节点里就要存储一个坐标(x,y)表示一个格子,另外还要存储四个指针。指针中有的为空,有的不为空,不为空的指针指向子节点,子节点保存邻居格子的坐标。这样做最大的问题是无法判定是否所有的格子都在树中。也许还要用一个二维数组作标志数组。

  假如用二维数组表示迷宫的格子。每个数组元素存储一个指向父节点的引用,这样也可以形成一个虚拟的树。于是就用一个n*n的二维数组,表示n*n个格子,每个数组元素(lattice)中有一个指向父节点的引用(father)。另外,为了能方便的获取格子的坐标,还要保存坐标信息。

  2.怎么构造这棵树?

  首先选定一个格子作为根节点。为了让迷宫的形状够随机,我选择随机生成一个坐标作为根节点。其实,选择确定的一个坐标也可以。

  然后,怎样往这棵树上增加节点呢?

  在这里我走了不少弯路,一开始想的是一种现在看来类似回溯的算法(当时还不知道回溯算法。。),但是时间复杂度很高,大概当迷宫为64*64的时候,算法就不出结果了。

  然后,又使用了一种扫深度搜索也是回溯描的方法,每次扫描在当前树中找一个节点,看它的邻居格子是否在树中,如果还没在树中,就将该邻居格子加入树中,如果已在树中,就看下一个邻居格子,如果该节点所有邻居格子都在树中了,就找下一个节点,继续同样的操作。另外为了让迷宫生成的随机,扫描的起始位置是随机的就可以了。但是,该方法生成的迷宫中的路径总是不够深,没有我想要的曲折深入的效果。毕竟是类似广度搜索的方法。而且,这样做总还像是靠蛮力,算法不够聪明简洁。

  最后,我终于想到使用深度搜索。。大概是因为数据结构已经学过了一年,又没太练,忘了不少,所以一直没想到这个应该第一想到的方法。。

  随机选择一个格子作为根节点,从它开始随机地深度搜索前进,开出一条路来,直到无路可走了,退回一步,换另一条路,再走到无路可走,回退一步,换另一条……如此循环往复,直到完全无路可走。。。其实也还是回溯。

  在程序里就是以下过程(详见代码中的createmaze()函数):

    随机选择一个格子作为根节点,将它压进栈里。

    然后在栈不为空的时候执行以下循环:

      取出一个格子,将它的intree标志设置为1,然后将它的所有不在树中的邻居格子压进栈里(顺序随机),并且让这些邻居格子的father指向该格子。

  解决了这两个问题,其余的画迷宫、显示路径、小球移动也就比较简单了。

代码

package maze;

import java.awt.color;
import java.awt.graphics;
import java.awt.event.keyadapter;
import java.awt.event.keyevent;
import java.util.random;
import java.util.stack;
import javax.swing.jframe;
import javax.swing.joptionpane;
import javax.swing.jpanel;
class lattice {
  static final int intree = 1;
  static final int notintree = 0;
  private int x = -1;
  private int y = -1;
  private int flag = notintree;
  private lattice father = null;
  public lattice(int xx, int yy) {
    x = xx;
    y = yy;
  }
  public int getx() {
    return x;
  }
  public int gety() {
    return y;
  }
  public int getflag() {
    return flag;
  }
  public lattice getfather() {
    return father;
  }
  public void setfather(lattice f) {
    father = f;
  }
  public void setflag(int f) {
    flag = f;
  }
  public string tostring() {
    return new string("(" + x + "," + y + ")\n");
  }
}
public class maze extends jpanel {
  private static final long serialversionuid = -8300339045454852626l;
  private int num, width, padding;// width 每个格子的宽度和高度
  private lattice[][] maze;
  private int ballx, bally;
  private boolean drawpath = false;
  maze(int m, int wi, int p) {
    num = m;
    width = wi;
    padding = p;
    maze = new lattice[num][num];
    for (int i = 0; i <= num - 1; i++)
      for (int j = 0; j <= num - 1; j++)
        maze[i][j] = new lattice(i, j);
    createmaze();
    setkeylistener();
    this.setfocusable(true);
  }
  private void init() {
    for (int i = 0; i <= num - 1; i++)
      for (int j = 0; j <= num - 1; j++) {
        maze[i][j].setfather(null);
        maze[i][j].setflag(lattice.notintree);
      }
    ballx = 0;
    bally = 0;
    drawpath = false;
    createmaze();
    // setkeylistener();
    this.setfocusable(true);
    repaint();
  }
  public int getcenterx(int x) {
    return padding + x * width + width / 2;
  }
  public int getcentery(int y) {
    return padding + y * width + width / 2;
  }

  public int getcenterx(lattice p) {
    return padding + p.gety() * width + width / 2;
  }
  public int getcentery(lattice p) {
    return padding + p.getx() * width + width / 2;
  }
  private void checkiswin() {
    if (ballx == num - 1 && bally == num - 1) {
      joptionpane.showmessagedialog(null, "you win !", "你走出了迷宫。",
          joptionpane.plain_message);
      init();
    }
  }
  synchronized private void move(int c) {
    int tx = ballx, ty = bally;
    // system.out.println(c);
    switch (c) {
      case keyevent.vk_left :
        ty--;
        break;
      case keyevent.vk_right :
        ty++;
        break;
      case keyevent.vk_up :
        tx--;
        break;
      case keyevent.vk_down :
        tx++;
        break;
      case keyevent.vk_space :
        if (drawpath == true) {
          drawpath = false;
        } else {
          drawpath = true;
        }
        break;
      default :
    }
    if (!isoutofborder(tx, ty)
        && (maze[tx][ty].getfather() == maze[ballx][bally]
            || maze[ballx][bally].getfather() == maze[tx][ty])) {
      ballx = tx;
      bally = ty;
    }
  }
  private void setkeylistener() {
    this.addkeylistener(new keyadapter() {
      public void keypressed(keyevent e) {
        int c = e.getkeycode();
        move(c);
        repaint();
        checkiswin();

      }
    });
  }
  private boolean isoutofborder(lattice p) {
    return isoutofborder(p.getx(), p.gety());
  }
  private boolean isoutofborder(int x, int y) {
    return (x > num - 1 || y > num - 1 || x < 0 || y < 0) ? true : false;
  }
  private lattice[] getneis(lattice p) {
    final int[] adds = {-1, 0, 1, 0, -1};// 顺序为上右下左
    if (isoutofborder(p)) {
      return null;
    }
    lattice[] ps = new lattice[4];// 顺序为上右下左
    int xt;
    int yt;
    for (int i = 0; i <= 3; i++) {
      xt = p.getx() + adds[i];
      yt = p.gety() + adds[i + 1];
      if (isoutofborder(xt, yt))
        continue;
      ps[i] = maze[xt][yt];
    }
    return ps;
  }
  private void createmaze() {
    random random = new random();
    int rx = math.abs(random.nextint()) % num;
    int ry = math.abs(random.nextint()) % num;
    stack<lattice> s = new stack<lattice>();
    lattice p = maze[rx][ry];
    lattice neis[] = null;
    s.push(p);
    while (!s.isempty()) {
      p = s.pop();
      p.setflag(lattice.intree);
      neis = getneis(p);
      int ran = math.abs(random.nextint()) % 4;
      for (int a = 0; a <= 3; a++) {
        ran++;
        ran %= 4;
        if (neis[ran] == null || neis[ran].getflag() == lattice.intree)
          continue;
        s.push(neis[ran]);
        neis[ran].setfather(p);
      }
    }
    // changefather(maze[0][0],null);
  }
  private void changefather(lattice p, lattice f) {
    if (p.getfather() == null) {
      p.setfather(f);
      return;
    } else {
      changefather(p.getfather(), p);
    }
  }
  private void clearfence(int i, int j, int fx, int fy, graphics g) {
    int sx = padding + ((j > fy ? j : fy) * width),
        sy = padding + ((i > fx ? i : fx) * width),
        dx = (i == fx ? sx : sx + width),
        dy = (i == fx ? sy + width : sy);
    if (sx != dx) {
      sx++;
      dx--;
    } else {
      sy++;
      dy--;
    }
    g.drawline(sx, sy, dx, dy);
  }
  protected void paintcomponent(graphics g) {
    super.paintcomponent(g);
    for (int i = 0; i <= num; i++) {
      g.drawline(padding + i * width, padding, padding + i * width,
          padding + num * width);
    }
    for (int j = 0; j <= num; j++) {
      g.drawline(padding, padding + j * width, padding + num * width,
          padding + j * width);
    }
    g.setcolor(this.getbackground());
    for (int i = num - 1; i >= 0; i--) {
      for (int j = num - 1; j >= 0; j--) {
        lattice f = maze[i][j].getfather();
        if (f != null) {
          int fx = f.getx(), fy = f.gety();
          clearfence(i, j, fx, fy, g);
        }
      }
    }
    g.drawline(padding, padding + 1, padding, padding + width - 1);
    int last = padding + num * width;
    g.drawline(last, last - 1, last, last - width + 1);
    g.setcolor(color.red);
    g.filloval(getcenterx(bally) - width / 3, getcentery(ballx) - width / 3,
        width / 2, width / 2);
    if (drawpath == true)
      drawpath(g);
  }
  private void drawpath(graphics g) {
    color path_color = color.orange, both_path_color = color.pink;
    if (drawpath == true)
      g.setcolor(path_color);
    else
      g.setcolor(this.getbackground());
    lattice p = maze[num - 1][num - 1];
    while (p.getfather() != null) {
      p.setflag(2);
      p = p.getfather();
    }
    g.filloval(getcenterx(p) - width / 3, getcentery(p) - width / 3,
        width / 2, width / 2);
    p = maze[0][0];
    while (p.getfather() != null) {
      if (p.getflag() == 2) {
        p.setflag(3);
        g.setcolor(both_path_color);
      }
      g.drawline(getcenterx(p), getcentery(p), getcenterx(p.getfather()),
          getcentery(p.getfather()));
      p = p.getfather();
    }
    g.setcolor(path_color);
    p = maze[num - 1][num - 1];
    while (p.getfather() != null) {
      if (p.getflag() == 3)
        break;
      g.drawline(getcenterx(p), getcentery(p), getcenterx(p.getfather()),
          getcentery(p.getfather()));
      p = p.getfather();
    }
  }
  public static void main(string[] args) {
    final int n = 30, width = 600, padding = 20, lx = 200, ly = 100;
    jpanel p = new maze(n, (width - padding - padding) / n, padding);
    jframe frame = new jframe("maze(按空格键显示或隐藏路径)");
    frame.getcontentpane().add(p);
    frame.setdefaultcloseoperation(jframe.exit_on_close);
    frame.setsize(width + padding, width + padding + padding);
    frame.setlocation(lx, ly);
    frame.setvisible(true);
  }
}

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网