首页 > 编程 > Java > 正文

简单行人疏散 Java版

2019-11-08 02:45:35
字体:
来源:转载
供稿:网友

一、整体思路

该程序实现了最简单的一个行人疏散模型,程序模拟行人从一个带有出口的封闭房间疏散,墙壁上有疏散方向标志,行人先移动到墙底,然后按照疏散方向往出口移动。

创建一个矩阵,空位设置为0,行人设置为1,墙壁设置为100,出口设置为1000,墙壁和出口的位置手工设置,本程序墙壁为于场地四周,出口位于上方墙壁的中点。行人的位置由随机产生或者手工设定。

具体规则如下:

1、  房间长m,宽n,行人密度p。

2、  行人在墙壁之外的无法看到出口,只能看到墙。

3、  行人会向离自己最近的墙面移动。

4、  墙面上有疏散方向。

5、  如果2人的下一点位置重合,各有50%的几率占据位置,另一个人则原地等待一步。

6、  行人到达出口时从系统移除。

二、具体程序

(一)、绘图类img.java

在绘图类的构造方法,完成界面的初始化,窗口的大小,关闭按钮的作用,将JPanel作为与矩阵相对应的场地。

    
public Img (){             //构造方法        jf = new JFrame("CA");        jp = new JPanel[m] [n];        jf.setLayout(new GridLayout(m,n,5,5));//mn 5 5 长m宽n 行间距5 列间距5        for (int i=0;i<m;i++){            for (intj=0;j<n;j++){                jp[i][j]=new JPanel();                jp[i][j].setBackground(Color.black);//布置背景色                jf.add(jp[i][j]);//将jp添加到jf            }        }        jf.setSize(500,500); //窗口大小        jf.setVisible(true);//显示窗口        jf.addWindowListener(newWindowAdapter() {//关闭按钮的作用            public voidwindowClosing(WindowEvent e) {                System.exit(0);            }        });    }

然后是创建行人位置的方法,也就是在矩阵中产生行人、墙壁、出口的方法。产生行人可以用随机方法creatRomNum,也可以用指定方法creatRegNum。

    

    
public int [][] creatRegNum(inta[][]){             //手工 创建矩阵             for(int i=0;i<m;i++){            for (intj=0;j<n;j++){                a[0][j]=100;//设置边缘  墙的值为100                a[i][0]=100;                a[i][m-1]=100;                a[n-1][j]=100;            }        }             for(int i=1;i<m-1;i++){                       for(int j=1;j<n-1;j++){                                a[i][i]=0;                       }             }             a[3][2]=1;             //a[3][2]=1;             //a[5][5]=1;             a[data.CX][data.CY]=1000;//出口             returna;    }    public int [][] creatRomNum(inta[][]){//随机创造矩阵         for (inti=0;i<m;i++){            for (intj=0;j<n;j++){                a[0][j]=100;//设置边缘  墙的值为100                a[i][0]=100;                a[i][m-1]=100;                a[n-1][j]=100;            }        }         for (inti=1;i<m-1;i++){            for (intj=1;j<n-1;j++){                if(Math.random()>p){                    a [i][j]=1;//行人代表1                }                else{                    a [i][j]=0;//空位代表0                }              }        }        a[data.CX][data.CY]=1000;//设置出口 值为1000        return a;    }

最后时绘图方法onColor,根据矩阵不同的数值设为不同的颜色。

public void onColor(inta[][]){             //绘图方法             for(int i=0;i<data.m;i++){                       for(int j=0;j<data.n;j++){                                switch(a[i][j]){//对应矩阵的值添加不同的颜色                         case 0: //空地为0  白色                            jp[i][j].setBackground(Color.white);                             break;                         case 1: //人为1  红色                             jp[i][j].setBackground(Color.red);                             break;                         case 100: //墙为100 蓝色                            jp[i][j].setBackground(Color.blue);                             break;                         case 1000: //出口为1000 黄色                             jp[i][j].setBackground(Color.yellow);                             break;                         default: //其他为黑色  一般用不到                            jp[i][j].setBackground(Color.black);                                }                       }             }           }

         (二)、行人类Peo.java

行人类包含移动方法peoMove,行人具体移动方法peoMoveDirection,判断下一点是否有人方法peoMoveDirectionDecect和行人到达出口方法peoMoveExit。

行人类中最为重要的就是移动方法peoMove。在此方法中,用到了3个数组:a[][],c[][]和d[][]。其中a是由绘图类产生并传进来的,用作判断。C是a的复制,采用循环赋值,在做出移动判断后将更改c的值。在移动之前,首先要判断:获取下一点移动的方向,下一点是否为墙或者人,下一点如果墙的话如何处理。最后交给具体移动方法peoMoveDirection来实现。

public int [][] peoMove(inta [][]){             int c[][]=new int [data.m][data.n];             int fr;//判断下一点为墙             int f;//移动方向             boolean moveOk;             for (int i=0;i<data.m;i++){ //循环赋值                       for (int j=0;j<data.n;j++){                                c[i][j]=a[i][j];                       }             }             for (int i=1;i<data.m-1;i++){                       for(int j=1;j<data.n-1;j++){                                //在进行移动之前先判断下一点是否能走                                //用d的值来判断                                f=rule.calRange(i, j, data.m, data.n,data.r);//判断方向                                if(a[i][j]==1){                                         fr=rule.stopChange(a, i, j);                                         if(fr==2){//判断墙                                                   f=rule.calRangeDouble(i,j);                                                   moveOk=peoMoveDirectionDecect(f,i, j, c);                                                   if(moveOk){                                                            c=peoMoveDirection(f,i, j, c);                                                   }                                                   else{                                                            c=peoMoveDirection(5,i, j, c);                                                   }                                         }                                         else{                                             c=peoMoveDirection(f,i, j, c);                                         }                                }                       }             }             return c;    }

行人具体移动方法peoMoveDirection和判断下一点是否有人方法peoMoveDirectionDecect大概差不多。peoMoveDirection方法是将下一点设置为1,将原来的点设置为0;peoMoveDirectionDecect是判断下一点是否为1,如果是,则返回false。

public int[][] peoMoveDirection(intf,int i,int j,int c [][]){             //行人移动  传入方向f 改变下一点的值             switch(f){        case 1:            c[i-1][j-1]=1;            c[i][j]=0;            break;        case 2:            c[i-1][j]=1;                             c[i][j]=0;            break;        case 3:            c[i-1][j+1]=1;            c[i][j]=0;            break;        case 4:            c[i][j-1]=1;            c[i][j]=0;            break;        case 5:            c[i][j]=1;            break;        case 6:            c[i][j+1]=1;            c[i][j]=0;            break;        case 7:            c[i+1][j-1]=1;            c[i][j]=0;            break;        case 8:            c[i+1][j]=1;            c[i][j]=0;            break;        case 9:            c[i+1][j+1]=1;            c[i][j]=0;            break;                   }             return c;    }    public booleanpeoMoveDirectionDecect(int f,int i,int j,int c[][]){             //判断行人下一点移动  传入方向f 如果下一点数值为1flag为false             boolean flag=true;             switch(f){        case 1:                 if(c[i-1][j-1]==1){                           flag=false;                 }            break;        case 2:                 if(c[i-1][j]==1){                           flag=false;                 }            break;        case 3:                 if(c[i-1][j+1]==1){                           flag=false;                 }            break;        case 4:                 if(c[i][j-1]==1){                           flag=false;                 }            break;        case 5:            c[i][j]=1;            break;        case 6:                 if(c[i][j+1]==1){                           flag=false;                 }            break;        case 7:                 if(c[i+1][j-1]==1){                           flag=false;                 }            break;        case 8:                 if(c[i+1][j]==1){                           flag=false;                 }            break;        case 9:                 if(c[i+1][j+1]==1){                           flag=false;                 }            break;                   }             return flag;    }

行人到达出口方法peoMoveExit,如果检测到行人在出口,则将行人移除系统。(这里为什么不整合到移动方法中呢?在主方法中,所有步骤运行后会有个赋值语句,将此次的结果覆盖原来的矩阵。如果写到移动方法中,会使出口方法失效)

public int [][] peoMoveExit(inta[][]){             //行人到达出口方法如果出去  设为0             boolean exitOk=false;             int c[][]=new int [data.m][data.n];             for (int i=0;i<data.m;i++){ //循环赋值                       for (int j=0;j<data.n;j++){                                c[i][j]=a[i][j];                       }             }             for(int i=0;i<data.m;i++){                       for(int j=0;j<data.n;j++){                                if(a[i][j]==1){                                         exitOk=rule.exitDose(a, i, j);                                    if(exitOk){                                             c[i][j]=0;                                             System.out.PRintln("跑出去一个");                                    }                                }                                                      }             }             return c;    }

         (三)、规则类Rule.Java

规则类包含方向判断方法calRange,遇到墙壁的方向判断calRangeDouble,判断墙壁方法stopChange和判断出口方法exitDose。

判断方法calRange,将行人距离四周墙壁的长度存入array中,然后用sort方法排序,取出第一个值,于4个方向的值相比较完成赋值。

public int calRange(int i,intj,int m,int n,int R){                   //判断方向的方法                   //将计算[i][j]与四周墙的距离 存入数组  使用sort方法进行排序                   //首先判断距离出口长度                   //其此将数组的最小值分别与4个值比较  得到具体方向        int f=5;        int north=i;        int east=m-j;        int south=n-i;        int west=j;        int ran[]=newint[4];        ran[0]=north;        ran[1]=east;        ran[2]=south;        ran[3]=west;        Arrays.sort(ran);            if(ran[0]==north){                f=2;            }            else if(ran[0]==east){                f=6;            }            else if(ran[0]==south){                f=8;            }            else if(ran[0]==west){                f=4;            }        return f;    }

遇到墙如何走方法calRangeDouble,根据位置不同,直接指定方法。

public int calRangeDouble(inti,int j){                   //沿着墙走时  判断遇到拐角的方法                   intf=0;//输出方向                   if(i==1){//如果行人在最上面                            if(j==1){//如果在在左边                                     f=6;//方向向右                            }                            elseif(j==data.n-2){//如果在最右边                                     f=4;                            }                            else{                                     if(j>=data.n/2){//如果在右半部分                                               f=4;                                     }                                     else{//左半边                                               f=6;                                     }                            }                   }                   if(i==data.m-2){//在最下面                            if(j==1){//最左边                                     f=2;                            }                            elseif(j==data.n-2){//最右边                                     f=2;                            }                            else{                                     if(j>=data.m/2){//右半部分                                               f=6;                                                                                   }                                     else{//左边部分                                               f=4;                                     }                            }                   }                   if(j==1){//在左边                            if(i==1){//左上角                                     f=6;                            }                            else{                                     f=2;                            }                   }                   if(j==data.n-2){//在右边                            if(i==1){//有上交                                     f=4;                            }                            else{                                     f=2;                            }                   }                   returnf;         }

判断墙的方法stopChange,遇到墙壁返回2。

public int stopChange(inta[][],int i,int j){                   //判断墙                   intfr=0; //fr 为判断下一步是否为墙                                                                 if(i==1||j==1||i==data.n-2|| j==data.m-2 ){                            fr=2;//fr=2 表示下一点为墙                   }                   returnfr;         }

判断出口方法exitDose,遇到出口返回TRUE。

public boolean exitDose(inta[][],int i,int j){                   //判断是否到达出口  如果到达出口 flag改为TRUE                   booleanflag=false;                   intexitId=data.n/2;                   if(i==1){                            if(j==exitId){                                     flag=true;                            }                            if(j==exitId+1){                                     flag=true;                            }                            if(j==exitId-1){                                     flag=true;                            }                   }                   returnflag;         }

(四)、主方法

       

  public class RunMain {         public static voidmain(String[] args) {                   Data data=new Data();                   Img img=new Img();                   Peo peo=new Peo();                    int a[][]=new int[data.m][data.n];                    int c[][]=new int[data.m][data.n];                    int d[][]=new int[data.m][data.n];                    a=img.creatRomNum(data.a);                    img.onColor(a);                    try {             Thread.sleep(15000);         } catch (Exception e) {             e.printStackTrace();         }                    for (int h=0;h<data.h;h++){                             c=peo.peoMove(a);                             d=peo.peoMoveExit(c);                             img.onColor(d);                             for (int i=0;i<data.m;i++){                                      for (int j=0;j<data.n;j++){                                                a[i][j]=d[i][j];                                      }                             }                             try {                      Thread.sleep(1000);                 } catch (Exception e) {                      e.printStackTrace();                 }                             System.out.println("第"+h+"次");                    }         }}

三、总结

经过这次编程,我又重新拾起快遗忘的Java。相对于matlab来说,Java最大的优势就是面向对象,各种方法有机结合。但是对新手不友好,需要先学一大堆Java知识,而matlab入手简单,简单学习就可以使用。

本程序离预定目标还差不少,最关键的是对于行人碰撞的解决不太可行。在房间中间的视野盲区,行人应该按随机方向行走,于是就有可能有2人的下一点在同一位置上,此时2人占据这点的概率应该是50%,但是程序是通过遍历数组来完成绘图的。这就导致位于前面的人就可以优先选择。虽然也可以通过将行人的一下步存到数组f中,然后在对其进行标识,从而产生50%的概率,但是这样做有点麻烦,因此在我想不使用矩阵,直接通过坐标将图绘出来,采用多线程,希望可以解决这个问题。


发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表