中级实训扩展任务

  刚刚实训答辩结束,在答辩上说了一句“我是C++的忠实粉丝”,于是TA们就都问我问题了。

  这次实训很水,基础部分几乎不需要让人了解底层,所以格外轻松。扩展部分设计的不大合适,所以经常会出现各种问题。写点东西,贴点代码,让后人乘荫吧。

  

第三部分扩展部分第一天:

实验要求
1. 利用二进制流读取Bitmap位图文件。注意,这里要求不能使用Java提供的API直接读取图像,根据二进制数据创建Image时可以使用API;
2. 把读取彩色图像转换成灰度图像;
3. 提取并且显示彩色图像各个色彩通道;
4. 把处理完的图像保存为bmp格式图像。注意,这里可以使用Java提供的API完成,但本文档不提供,希望各位同学自行上网查找资料自学。如果学有余力的同学,可以实现按照二进制流输出保存bmp图像;

5. 编写Junit测试程序,测试输出的图片是否与goal文件夹下的图片一致。(比较位图宽度、位图高度以及像素值)

    个人补充:Bitmap是windows下的格式,比较无聊地被拿到Linux下去读取,wiki上给的链接介绍Bitmap的格式,网上再稍微搜一下就对它有更深的理解了。我对bitmap有点了解后,就到MSDN搜索bitmap的数据结构,模仿其数据结构去保存数据。代码如下:

   

  1 package ImageIOImplement;
  2 import java.awt.*;  
  3 import java.awt.image.*;  
  4 import javax.imageio.*;  
  5 import java.io.*;  
  6 import java.lang.String;
  7 import imagereader.IImageIO;
  8 
  9 public class ImageIOImplement implements IImageIO
 10 {
 11     /* 位图文件头,模仿windows下的结构体,
 12      * 详细见:http://msdn.microsoft.com/en-us/library/windows/desktop/dd183374(v=vs.85).aspx
 13      */
 14     public class BITMAPFILEHEADER
 15     {
 16         public int bfType; // 位图文件的类型,必须为BM
 17         public int bfSize; // 位图文件的大小,以字节为单位
 18         public int bfReserved1; // 位图文件保留字,必须为0
 19         public int bfReserved2; // 位图文件保留字,必须为0
 20         public int bfOffBits; // 位图数据的起始位置,以相对于位图
 21         
 22         /*保存位图头
 23         * 字节 #0-1    保存位图文件的标识符,这两个字节的典型数据是BM。
 24         * 字节 #2-5    使用一个dword保存位图文件大小。
 25         * 字节 #6-9    是保留部分,留做以后的扩展使用,对实际的解码格式没有影响。
 26         * 字节 #10-13    保存位图数据位置的地址偏移,也就是起始地址。
 27         * 注:由以上可知,只有字节#2-5是需要保存的。
 28         */
 29         public BITMAPFILEHEADER(FileInputStream pFile)
 30         {
 31             int BitMapHeadRSize = 14;
 32             byte pBITMAPFILEHEADER[] = new byte[BitMapHeadRSize];
 33             try
 34             {
 35                 pFile.read(pBITMAPFILEHEADER, 0, BitMapHeadRSize);
 36                 bfType = 16973;         //ox424D, BM
 37                 //为何要&呢?因为byte(8位)转int(32位)时,需要补24位,当byte数据是无符号数时,则补0,有符号时,则补1
 38                 bfSize = (((int)pBITMAPFILEHEADER[5] & 0xff) << 24)  
 39                           | (((int)pBITMAPFILEHEADER[4] & 0xff) << 16)  
 40                           | (((int)pBITMAPFILEHEADER[3] & 0xff) << 8)  
 41                           | ((int)pBITMAPFILEHEADER[2] & 0xff);  
 42                 bfReserved1 = 0;
 43                 bfReserved2 = 0;
 44                 //bfOffBits暂时不进行处理 
 45             }
 46             //IO读取异常,FileInputStream和FileOutStream必须使用异常处理,略无奈
 47             catch(IOException e)
 48             { 
 49                 e.printStackTrace(); 
 50             }
 51         }
 52         
 53     }
 54     
 55     /* 位图信息头,模仿windows下的结构体
 56      * 详细见:http://msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
 57      */
 58     public class  BITMAPINFOHEADER
 59     {
 60         public int biSize; // 本结构所占用字节数
 61         public int biWidth; // 位图的宽度,以像素为单位
 62         public int biHeight; // 位图的高度,以像素为单位
 63         public int biPlanes; // 目标设备的级别,必须为1
 64         public int biBitCount;// 每个像素所需的位数,必须是1(双色),
 65         // 4(16色),8(256色)或24(真彩色)之一
 66         public int biCompression; // 位图压缩类型,必须是 0(不压缩),
 67         // 1(BI_RLE8压缩类型)或2(BI_RLE4压缩类型)之一
 68         public int biSizeImage; // 位图的大小,以字节为单位
 69         public int biXPelsPerMeter; // 位图水平分辨率,每米像素数
 70         public int biYPelsPerMeter; // 位图垂直分辨率,每米像素数
 71         public int biClrUsed;// 位图实际使用的颜色表中的颜色数
 72         public int biClrImportant;// 位图显示过程中重要的颜色数
 73         
 74         //
 75         public int ColorModel[];
 76         /* 保存位图信息
 77          * 字节 #14-17    定义以下用来描述影像的区块(BitmapInfoHeader)的大小。它的值是:40 - Windows 3.2、95、NT、12 - OS/2 1.x、240 - OS/2 2.x
 78          * 字节 #18-21    保存位图宽度(以像素个数表示)。
 79          * 字节 #22-25    保存位图高度(以像素个数表示)。
 80          * 字节 #26-27    保存所用彩色位面的个数。不经常使用。
 81           * 字节 #28-29    保存每个像素的位数,它是图像的颜色深度。常用值是1、4、8(灰阶)和24(彩色)。
 82          * 字节 #30-33    定义所用的压缩算法。允许的值是0、1、2、3、4、5。
 83          *                     0 - 没有压缩(也用BI_RGB表示)
 84          *                     1 - 行程长度编码 8位/像素(也用BI_RLE8表示)
 85          *                     2 - 行程长度编码4位/像素(也用BI_RLE4表示)
 86          *                     3 - Bit field(也用BI_BITFIELDS表示)
 87          *                     4 - JPEG图像(也用BI_JPEG表示)
 88          *                     5 - PNG图像(也用BI_PNG表示)
 89          *                    然而,由于大多数位图文件都是不压缩的,所以最常用的值是0。
 90          * 字节 #34-37    保存图像大小。这是原始(:en:raw)位图数据的大小,不要与文件大小混淆。
 91          * 字节 #38-41    保存图像水平方向分辨率。
 92          * 字节 #42-45    保存图像竖值方向分辨率。
 93          * 字节 #46-49    保存所用颜色数目。
 94          * 字节 #50-53    保存所用重要颜色数目。当每个颜色都重要时这个值与颜色数目相等。
 95          * */
 96         public BITMAPINFOHEADER(FileInputStream pFile)
 97         {
 98             int bitMapInfoHeaderSize = 40;
 99             byte pBITMAPINFOHEADER[] = new byte[bitMapInfoHeaderSize];
100             try
101             {
102                 pFile.read(pBITMAPINFOHEADER, 0, bitMapInfoHeaderSize);
103                 
104                 biSize = (((int)pBITMAPINFOHEADER[3] & 0xff) << 24)  
105                          | (((int)pBITMAPINFOHEADER[2] & 0xff) << 16)  
106                          | (((int)pBITMAPINFOHEADER[1] & 0xff) << 8)  
107                          | (int)pBITMAPINFOHEADER[0] & 0xff;  
108             
109                 biWidth = (((int)pBITMAPINFOHEADER[7] & 0xff) << 24)  
110                         | (((int)pBITMAPINFOHEADER[6] & 0xff) << 16)  
111                         | (((int)pBITMAPINFOHEADER[5] & 0xff) << 8)  
112                         | (int)pBITMAPINFOHEADER[4] & 0xff;  
113 
114                 biHeight = (((int)pBITMAPINFOHEADER[11] & 0xff) << 24)  
115                         | (((int)pBITMAPINFOHEADER[10] & 0xff) << 16)  
116                         | (((int)pBITMAPINFOHEADER[9] & 0xff) << 8)  
117                         | (int)pBITMAPINFOHEADER[8] & 0xff;  
118 
119 
120                 biPlanes = (((int)pBITMAPINFOHEADER[13] & 0xff) << 8) 
121                            | (int)pBITMAPINFOHEADER[12] & 0xff;  
122 
123 
124                 biBitCount = (((int)pBITMAPINFOHEADER[15] & 0xff) << 8) 
125                             | (int)pBITMAPINFOHEADER[14] & 0xff;  
126 
127               
128                 biCompression = (((int)pBITMAPINFOHEADER[19]) << 24)  
129                               | (((int)pBITMAPINFOHEADER[18]) << 16)  
130                               | (((int)pBITMAPINFOHEADER[17]) << 8)  
131                               | (int)pBITMAPINFOHEADER[16];  
132 
133                 biSizeImage = (((int)pBITMAPINFOHEADER[23] & 0xff) << 24)  
134                             | (((int)pBITMAPINFOHEADER[22] & 0xff) << 16)  
135                             | (((int)pBITMAPINFOHEADER[21] & 0xff) << 8)  
136                             | (int)pBITMAPINFOHEADER[20] & 0xff;  
137 
138                 biXPelsPerMeter = (((int)pBITMAPINFOHEADER[27] & 0xff) << 24)  
139                                   | (((int)pBITMAPINFOHEADER[26] & 0xff) << 16)  
140                                   | (((int)pBITMAPINFOHEADER[25] & 0xff) << 8)  
141                                   | (int)pBITMAPINFOHEADER[24] & 0xff;  
142       
143 
144                 biYPelsPerMeter = (((int)pBITMAPINFOHEADER[31] & 0xff) << 24)  
145                                 | (((int)pBITMAPINFOHEADER[30] & 0xff) << 16)  
146                                 | (((int)pBITMAPINFOHEADER[29] & 0xff) << 8)  
147                                 | (int)pBITMAPINFOHEADER[28] & 0xff;  
148 
149                 biClrUsed = (((int)pBITMAPINFOHEADER[35] & 0xff) << 24)  
150                           | (((int)pBITMAPINFOHEADER[34] & 0xff) << 16)  
151                           | (((int)pBITMAPINFOHEADER[33] & 0xff) << 8)  
152                           | (int)pBITMAPINFOHEADER[32] & 0xff;  
153 
154                 biClrImportant = (((int)pBITMAPINFOHEADER[39] & 0xff) << 24)  
155                                | (((int)pBITMAPINFOHEADER[38] & 0xff) << 16)  
156                                | (((int)pBITMAPINFOHEADER[37] & 0xff) << 8)  
157                                | (int)pBITMAPINFOHEADER[36] & 0xff; 
158                 
159                 if(biBitCount == 24)
160                 {
161                     //构建位数组
162                     //由于像素使用的字节若不是4的倍数,则会自动扩大,由此产生空白。因此我们需要在一开始计算出空白的大小
163                     int biPad = (biSizeImage / biHeight) - biWidth * 3; 
164                     if(biPad == 4)
165                     {  
166                         biPad = 0;  
167                     }  
168                     ColorModel = new int[biHeight * biWidth];  
169                     //由此构建数组  
170                     byte brgb[] = new byte[biSizeImage];  
171                     pFile.read(brgb, 0, biSizeImage);  
172                     
173                     int index = 0;  
174                     for(int j = 0; j < biHeight; j++)
175                     {  
176                         for(int i = 0; i < biWidth; i++)
177                         {  
178                             //为何要&255?透明度呀卧槽...
179                             ColorModel[biWidth * (biHeight - j - 1) + i] =  
180                                                             (255 & 0xff) << 24  
181                                                         | (((int)brgb[index + 2] & 0xff) << 16)  
182                                                         | (((int)brgb[index + 1] & 0xff) << 8)  
183                                                         | (int)brgb[index] & 0xff;  
184                             index += 3;  
185                         }  
186                         index += biPad;  
187                     }
188                 }
189                 else
190                 {
191                     //抛出异常或打印提醒
192                     System.out.println("不是24bit,暂时不能处理");
193                 }
194             }
195             //IO读取异常,FileInputStream和FileOutStream必须使用异常处理,略无奈
196             catch(IOException e)
197             { 
198                 e.printStackTrace(); 
199             }
200         }  
201          
202         public Image getImage()
203         {
204             //MemoryImageSouce来自ImageProducer
205             Image pImage = null;
206             pImage = Toolkit.getDefaultToolkit().createImage(new MemoryImageSource(
207                                         this.biWidth, this.biHeight, 
208                                         this.ColorModel, 0, this.biWidth));
209             return pImage;
210         }
211     }
212     
213     public Image myRead(String bmpFileName)
214     {
215         //我们要读取到Image
216         Image pImage = null;
217         try
218         {
219             //读取文件
220             FileInputStream pFile = new FileInputStream(bmpFileName);
221                 
222             //保存位图头,弱弱地说这个暂时没用到,在保存文件时用了API
223             BITMAPFILEHEADER pBITMAPFILEHEADER = new BITMAPFILEHEADER(pFile);
224                 
225             // 保存位图信息
226             BITMAPINFOHEADER pBITMAPINFOHEADER = new BITMAPINFOHEADER(pFile);
227                 
228             pImage = pBITMAPINFOHEADER.getImage();
229             
230             pFile.close();
231             
232             return pImage;
233         }
234         //IO读取异常,FileInputStream和FileOutStream必须使用异常处理,略无奈
235         catch(IOException e)
236         { 
237             e.printStackTrace(); 
238         }
239         return null;
240     }
241     
242     //根据二进制数据创建Image时可以使用API
243     public Image myWrite(Image pImage, String saveFileName)
244     {
245         try  
246         {  
247             File pImgFile = new File(saveFileName + ".bmp");
248             BufferedImage pBufferedImage = new BufferedImage(pImage.getWidth(null), 
249                                                     pImage.getHeight(null), 
250                                                     BufferedImage.TYPE_INT_RGB);
251             Graphics pGraphics = pBufferedImage.getGraphics();
252             pGraphics.drawImage(pImage, 0, 0, null); 
253             pGraphics.dispose();
254             ImageIO.write(pBufferedImage, "bmp", pImgFile); 
255         }  
256         catch (Exception e)  
257         {  
258             e.printStackTrace(System.out);  
259         }  
260         
261         return pImage;
262     }
263 }

至于如何提取不同颜色通道,看下面的代码注释就了解了。如果大家学完第一第二部分,能够去看一下GridWorld的WorldFrame,看看GUIController和Grid如何分离,我记得里面有一个转灰色的方法,就可以模仿他的实现。

  1 package ImageIOImplement;
  2 import imagereader.IImageProcessor;  
  3 
  4 import java.awt.*;  
  5 import java.awt.image.*; 
  6 
  7 /*
  8  * 根据不同颜色通道的提取实现不同的filterRGB方法。到处查看声明发现filterRGB是一个abstract方法。
  9  * 查看调用发现是在filterIndexColorModel和filterRGBPixels中调用。
 10  * 查看setColorModel,发现这个条件canFilterIndexColorModel && (model instanceof IndexColorModel)决定了是否调用自己实现的提取色彩通道的方法。
 11  * 貌似必须这样做,所以就
 12  *  */
 13 public class ImageProcessor implements IImageProcessor  
 14 {
 15     //R
 16     public Image showChanelR(Image sourceImage)
 17     {
 18         RedFilter filter = new RedFilter();
 19         Toolkit kit = Toolkit.getDefaultToolkit();
 20         Image newimg = kit.createImage(new FilteredImageSource(sourceImage.getSource(), filter));
 21         return newimg;
 22     }
 23     //G
 24     public Image showChanelG(Image sourceImage)
 25     {
 26         GreenFilter filter = new GreenFilter();
 27         Toolkit kit = Toolkit.getDefaultToolkit();
 28         Image newimg = kit.createImage(new FilteredImageSource(sourceImage.getSource(), filter));
 29         return newimg;
 30     }
 31     //B
 32     public Image showChanelB(Image sourceImage)
 33     {
 34         BlueFilter filter = new BlueFilter();
 35         Toolkit kit = Toolkit.getDefaultToolkit();
 36         Image newimg = kit.createImage(new FilteredImageSource(sourceImage.getSource(), filter));
 37         return newimg;
 38     }
 39     //Gray
 40     public Image showGray(Image sourceImage)
 41     {
 42         GrayFilter filter = new GrayFilter();
 43         Toolkit kit = Toolkit.getDefaultToolkit();
 44         Image newimg = kit.createImage(new FilteredImageSource(sourceImage.getSource(), filter));
 45         return newimg;
 46     }
 47     //实现自己的红色通道提取
 48     class RedFilter extends RGBImageFilter
 49     {
 50         public RedFilter()
 51         {
 52             //查看RGBImageFilter的setColorModel方法,发现
 53             //这个条件canFilterIndexColorModel && (model instanceof IndexColorModel)
 54             //决定着是否调用自己的提取方法,所以将canFilterIndexColorModel设置为true
 55             canFilterIndexColorModel = true;    
 56         }
 57         public int filterRGB(int x, int y, int rgb)
 58         {
 59             return (rgb & 0xffff0000);
 60         }
 61     }
 62     //Green通道提取
 63     class GreenFilter extends RGBImageFilter
 64     {
 65         public GreenFilter()
 66         {
 67             canFilterIndexColorModel = true;
 68         }
 69         public int filterRGB(int x, int y, int rgb)
 70         {
 71             return (rgb & 0xff00ff00);
 72         }
 73     }
 74     //Blue提取
 75     class BlueFilter extends RGBImageFilter
 76     {
 77         public BlueFilter()
 78         {
 79             canFilterIndexColorModel = true;
 80         }
 81         public int filterRGB(int x, int y, int rgb)
 82         {
 83             return (rgb & 0xff0000ff);
 84         }
 85     }
 86     //Gray提取
 87     class GrayFilter extends RGBImageFilter
 88     {
 89         public GrayFilter()
 90         {
 91             canFilterIndexColorModel = true;
 92         }
 93         public int filterRGB(int x, int y, int rgb)
 94         {
 95             int gray = (int)(((rgb & 0x00ff0000)>>16)*0.299 + 
 96                             ((rgb & 0x0000ff00)>>8)*0.587 + 
 97                             (rgb & 0x000000ff)*0.114);
 98             return (rgb & 0xff000000)+(gray<<16)+(gray<<8)+gray;
 99         } 
100     }
101 }

Runner:

package ImageIOImplement;
import imagereader.Runner;  

public class MyRunner 
{
    public static void main(String[] args)
    {  
        ImageIOImplement imgIO = new ImageIOImplement();  
        ImageProcessor processor = new ImageProcessor();  
        Runner.run(imgIO, processor);  
    }  
}

    单元测试嘛,我从开始学Junit的时候就去GitHub上查看官方文档(刚开始查到一个乱七八糟的文档然后被坑了),查看里面的参数化测试方法(链接:https://github.com/junit-team/junit/wiki/Parameterized-tests),里面给了个例子,非常容易懂,有一个方法定义了不同情境下的数据,然后在构造函数里面获取数据,然后有几组数据就会测试几次。

    下面的代码也写得很清楚了,不过记得在读取图片的时候,要使用绝对地址。想要使用相对地址的貌似需要改点东西~

package ImageIOImplement;

import static org.junit.Assert.*;

import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.*;

import java.io.FileInputStream;
import java.util.Collection;
import java.util.Arrays;  

import java.awt.*;
import javax.imageio.*;
import java.lang.String;
import org.junit.runner.RunWith;  
import org.junit.runners.Parameterized;  
import org.junit.runners.Parameterized.Parameters;  

@RunWith(Parameterized.class)
public class ImageReaderTest
{
    //private String filePath0;
    //private String filePath1;
    private FileInputStream pFile0;
    private FileInputStream pFile1;
    private Image pImage0;
    private Image pImage1;

    
    @Parameters   
    public static Collection<Object[]> prepareData()
    {  
         return Arrays.asList(new Object[][]{
                 {"/home/gumc/Bitmap/bmptest/my/1_blue_goal.bmp", "/home/gumc/Bitmap/bmptest/goal/1_blue_goal.bmp"},
                 {"/home/gumc/Bitmap/bmptest/my/1_red_goal.bmp", "/home/gumc/Bitmap/bmptest/goal/1_red_goal.bmp"},
                 {"/home/gumc/Bitmap/bmptest/my/1_green_goal.bmp", "/home/gumc/Bitmap/bmptest/goal/1_green_goal.bmp"},
                 {"/home/gumc/Bitmap/bmptest/my/1_gray_goal.bmp", "/home/gumc/Bitmap/bmptest/goal/1_gray_goal.bmp"},
                 {"/home/gumc/Bitmap/bmptest/my/2_blue_goal.bmp", "/home/gumc/Bitmap/bmptest/goal/2_blue_goal.bmp"},
                 {"/home/gumc/Bitmap/bmptest/my/2_red_goal.bmp", "/home/gumc/Bitmap/bmptest/goal/2_red_goal.bmp"},
                 {"/home/gumc/Bitmap/bmptest/my/2_green_goal.bmp", "/home/gumc/Bitmap/bmptest/goal/2_green_goal.bmp"},
                 {"/home/gumc/Bitmap/bmptest/my/2_gray_goal.bmp", "/home/gumc/Bitmap/bmptest/goal/2_gray_goal.bmp"}});
    }  
    
    public ImageReaderTest(String file1, String file2) throws Exception 
    {
        //this.filePath0 = file1;
        //this.filePath1 = file2;
        this.pFile0 = new FileInputStream(file1);
        this.pFile1 = new FileInputStream(file2);
        this.pImage0 = ImageIO.read(pFile0);
        this.pImage1 = ImageIO.read(pFile1);
    }
    
    @Before
    public void setUp() throws Exception {
    }

    //比较分辨率(长宽像素值都相等)
    @Test
    public void testResolution()
    {
        assertEquals(pImage0.getHeight(null), pImage1.getHeight(null));
        assertEquals(pImage0.getWidth(null), pImage1.getWidth(null));
    }
    
    //比较像素值
    @Test
    public void testPixValue() throws Exception 
    {
        int sizeImage = pImage0.getHeight(null) * pImage1.getWidth(null);
            
        //将两个图片的位图数据部分全部读出,比较。
        byte imageColor0[] = new byte[sizeImage];
        byte imageColor1[] = new byte[sizeImage];
            
        //跳过不属于位图信息的部分
        pFile0.skip(54);
        pFile1.skip(54);
            
        pFile0.read(imageColor0, 0, sizeImage);
        pFile1.read(imageColor1, 0, sizeImage);
        //是否相等
        assertEquals(imageColor1, imageColor1);    
    }

}

 

第三部分扩展部分第二天:

 

    1. 无环路迷宫在数据结构上表现为一棵树,采用深度优先搜索算法就可以走出迷宫。本实验的目的是让同学们学习、理解和应用深度优先搜索算法。本实验要求同学们在改进的Grid World软件装置中实现深度优先搜索算法,从而使虫子走出迷宫。

  就是用栈实现深搜嘛,刚开始一直纠结为何是Stack<ArrayList<Location>>来保存状态,而不是Stack<Location>来保存状态。想了很久才知道,原来他在栈里面就实现了保存路径和剪枝,这样的话就不用再去记录是否走过可否再走。例如我下面用栈的变化来解释为何要使用ArrayList<Location>:

  0: (0, 0)                              //栈的初始状态

      1:   (0, 0),  (0, 1)    |     (0,1)                       //走到(0,1)是的栈状态,我用|分割不同的ArrayList<Location>

      2:   (0, 0),  (0, 1)    |     (0, 1),  (0, 2) |  (0, 2)            //走到(0,2)是的栈状态

  3:   (0, 0),  (0, 1)    |     (0, 1),  (0, 2)                //假如不能再走,则pop一下

  4:   (0, 0),  (0, 1)    |     (0, 1),  (0, 2), (1, 1) |  (1, 1)        //取出(0, 1)即栈顶链表的第一个元素,找到可以走的点(1, 1),为何不是(0, 2)了呢?因为已经在链表中,证明已经走过了。

  这样就对为何使用ArrayList有所理解了。

  下面是我实现的代码,写得很丑,非常丑,因为那天状态挺糟糕的,所以几乎写不出代码来。

  1 package info.gridworld.maze;
  2 
  3 import info.gridworld.actor.Actor;
  4 
  5 /**
  6  * A <code>MazeBug</code> can find its way in a maze. <br />
  7  * The implementation of this class is testable on the AP CS A and AB exams.
  8  */
  9 public class MazeBug extends Bug 
 10 {
 11     //public Location next;
 12     ///public Location last;
 13     //public Location last;
 14     public boolean isEnd = false;
 15     public Stack<ArrayList<Location>> crossLocation = new Stack<ArrayList<Location>>();
 16     public Integer stepCount = 0;
 17     boolean hasShown = false;//final message has been shown
 18     
 19     private int direction[] = {Location.NORTH, Location.EAST, Location.SOUTH, Location.WEST};
 20     class Probability
 21     {
 22         //North, East, South, West
 23         public int dire[] = {1, 1, 1, 1};
 24         public Location getNextLocation(ArrayList<Location> array, Location nowLoc)
 25         {
 26             int size = array.size();
 27             int k = nowLoc.getDirectionToward(array.get(0)) / 90;
 28             int max = dire[k];
 29             
 30             for (int i = 1; i < size; i++)
 31             {
 32                 int j = nowLoc.getDirectionToward(array.get(i)) / 90;
 33                 int tmp = dire[j];
 34                 if (max < tmp)
 35                 {
 36                     max = tmp;
 37                     k = j;
 38                 }
 39             }
 40             return nowLoc.getAdjacentLocation(direction[k]);
 41         }
 42         
 43         public void increase(int oneDirection)
 44         {
 45             if (oneDirection < 0)
 46                 throw new IllegalArgumentException("Direction " + oneDirection + " is not valid");
 47             dire[oneDirection / 90]++; 
 48         }
 49         public void descrease(int oneDirection)
 50         {
 51             if (oneDirection < 0)
 52                 throw new IllegalArgumentException("Direction " + oneDirection + " is not valid");
 53             dire[oneDirection / 90]--; 
 54         }
 55     }
 56     //四个方向的概率
 57     Probability prob = new Probability();
 58     
 59     /**
 60      * Constructs a box bug that traces a square of a given side length
 61      * 
 62      * @param length
 63      *            the side length
 64      */
 65     public MazeBug() 
 66     {
 67         setColor(Color.GREEN);
 68     }
 69 
 70     /**
 71      * Moves to the next location of the square.
 72      */
 73     public void act() 
 74     {
 75         if (stepCount == 0)
 76         {
 77             ArrayList<Location> pLocs = new ArrayList<Location>();
 78             pLocs.add(this.getLocation());
 79             crossLocation.push(pLocs);
 80         }
 81         boolean willMove = canMove();
 82         if (isEnd == true)
 83         {
 84             //结束
 85             if (hasShown == false) 
 86             {
 87                 String msg = stepCount.toString() + " steps";
 88                 JOptionPane.showMessageDialog(null, msg);
 89                 hasShown = true;
 90             }
 91         } 
 92         else if (willMove)
 93         {    
 94             //移动
 95             move();
 96             stepCount++;
 97         }
 98         else
 99         {
100             //回溯
101             backtracking();
102             stepCount++;
103         }
104     }
105 
106     /**
107      * Find all positions that can be move to.
108      * 
109      * @param loc
110      *            the location to detect.
111      * @return List of positions.
112      */
113     //取得除了来时的Location外的所有下一步可走的Location
114     public ArrayList<Location> getValid(Location loc) 
115     {
116         Grid<Actor> gr = getGrid();
117         if (gr == null)
118             return null;
119         
120         ArrayList<Location> valid = new ArrayList<Location>();
121         if (crossLocation.size() > 0)
122         {
123             ArrayList<Location> first = crossLocation.pop();
124             ArrayList<Location> second = null;
125             if (crossLocation.size() > 0)
126                 second = crossLocation.peek();
127             for (int i = 0; i < 4; i++)
128             {
129                 Location next = loc.getAdjacentLocation(direction[i]);
130                 Actor pActor = null;
131                 if (gr.isValid(next))
132                 {
133                     pActor = (Actor)gr.get(next);
134                     if (pActor instanceof Rock && pActor.getColor().equals(new Color(255, 0, 0)))
135                     {
136                         isEnd = true;
137                         valid.add(next);
138                         this.setDirection(this.getLocation().getDirectionToward(next));
139                         this.moveTo(next);
140                     }    
141                     else if (pActor == null)
142                     {
143                         if (!first.contains(next))
144                         {
145                             valid.add(next);
146                         }
147                     }
148                 }
149             }
150             if (valid.size() == 0)
151             {
152                 
153                 for (int i = 0; i < 4; i++)
154                 {
155                     Location next = loc.getAdjacentLocation(direction[i]);
156                     if (gr.isValid(next))
157                     {
158                         Actor pActor = (Actor)gr.get(next);
159                         if (pActor instanceof Flower)
160                         {
161                             if (second != null && !second.contains(next) && !first.contains(next))
162                             {
163                                 valid.add(next);
164                             }
165                             else if (second == null && !first.contains(next))
166                             {
167                                 valid.add(next);
168                             }
169                         }
170                     }
171                 }
172             }
173             crossLocation.add(first);
174         }
175         
176         return valid;
177     }
178 
179     /**
180      * Tests whether this bug can move forward into a location that is empty or
181      * contains a flower.
182      * 
183      * @return true if this bug can move.
184      */
185     public boolean canMove() 
186     {
187         if (this.getValid(this.getLocation()).size() > 0)
188             return true;
189         return false;
190     }
191     /**
192      * Moves the bug forward, putting a flower into the location it previously
193      * occupied.
194      */
195     public void move() 
196     {
197         Grid gr = this.getGrid();
198         if (gr == null)
199             return;
200         //保存原来的位置
201         Location loc = this.getLocation();
202         //获取所有可移动的点
203         ArrayList<Location>pLocs = this.getValid(this.getLocation());
204         //根据概率获取概率最大的点
205         Location next = prob.getNextLocation(pLocs, this.getLocation());
206         //移动到该点
207         if (gr.isValid(next))
208         {
209             this.setDirection(this.getLocation().getDirectionToward(next));
210             this.moveTo(next);
211             prob.increase(this.getDirection());
212         }
213         else
214         {
215             removeSelfFromGrid();
216             return;
217         }
218         
219         Flower flower = new Flower(getColor());
220         flower.putSelfInGrid(gr, loc);
221         
222         pLocs = crossLocation.pop();
223         pLocs.add(next);
224         crossLocation.push(pLocs);
225         
226         pLocs = new ArrayList<Location>();
227         pLocs.add(next);
228         crossLocation.push(pLocs);
229     }
230     
231     private void backtracking()
232     {
233         Grid gr = this.getGrid();
234         if (gr == null)
235             return ;
236         if (crossLocation.size() > 0)
237         {
238             crossLocation.pop();
239             if (crossLocation.size() > 0)
240             {
241                 Location loc = this.getLocation();
242                 ArrayList<Location> pLocs = crossLocation.peek();
243                 Location next = pLocs.get(0);
244                 if (gr.isValid(next))
245                 {
246                     this.setDirection(loc.getDirectionToward(next));
247                     this.moveTo(next);
248                 }
249                 else
250                 {
251                     removeSelfFromGrid();
252                     return;
253                 }
254                 Flower flower = new Flower(getColor());
255                 flower.putSelfInGrid(gr, loc);
256             }
257         }
258     }
259 }

 

    2. 参考已实现的MazeBug,定义一个继承Bug类的MazeDigBug类,需要实现以下功能。

    (1)MazeDigBug能在一张N*N的布满石头的grid中挖出一个迷宫。注意虫子每次只能前进一步,不能出现闪烁飞行的情况。

    (2)参考FinalMaze01.txt中的格式,自行学习Java的文件IO,定义好迷宫的终点并将迷宫输出到文件。然后将迷宫加载,并使用第一部分的MazeBug走出迷宫。

    输出可以在地图生成时IO将文件地图输出(不需要GUI参与);也可以修改WorldFrame.java,仿造其中Load Map的实现添加Save Map按钮(保存地图时可以借助其中的saveMap方法将地图输出)。

注:因为状态太差了,所以几乎写不出代码。所以我就随意实现了一个只能挖奇数迷宫的代码,也就是一次走两格。本来想继续改成能挖出偶数的,但是好困...每个月总有那么一些天不想突然像来大姨爸一样。

  

  1 package info.gridworld.maze;
  2 
  3 import javax.swing.JOptionPane;
  4 
  5 //迷宫生成虫,把石头当作路径,所以每次应该尝试走两步。一步是穿过墙,一步是到达新可能开辟通道的地方
  6 public class MazeDigBug extends Bug
  7 {
  8     private Location next;
  9     private boolean isEnd = false;
 10     boolean hasShown = false;
 11     private Stack<Location> pathLocation = new Stack();
 12     private boolean isContinuing = false;
 13     private boolean isBacktracing = false;
 14     private int direction[] = {Location.NORTH, Location.EAST, Location.SOUTH, Location.WEST};
 15     
 16     public MazeDigBug()
 17     {
 18         setColor(Color.green);
 19     }
 20 
 21     public void act()
 22     {
 23         boolean willMove = canMove();
 24         if (this.isEnd)
 25         {
 26             if (!this.hasShown) 
 27             {
 28                 String msg = "OK";
 29                 JOptionPane.showMessageDialog(null, msg);
 30                 this.hasShown = true;
 31             }
 32         } 
 33         else if (willMove) 
 34         {
 35             move();
 36         }
 37     }
 38 
 39     public ArrayList<Integer> getValid(Location loc)
 40     {
 41         Grid gr = getGrid();
 42         if (gr == null)
 43             return null;
 44         ArrayList valid = new ArrayList();
 45         for (int i = 0; i < 4; i++)
 46         {
 47             Location nextLoc0 = loc.getAdjacentLocation(direction[i]);
 48             Location nextLoc1 = nextLoc0.getAdjacentLocation(direction[i]);
 49             if (gr.isValid(nextLoc0) && gr.isValid(nextLoc1))
 50             {
 51                 Actor pActor0 = (Actor)gr.get(nextLoc0);
 52                 Actor pActor1 = (Actor)gr.get(nextLoc1);
 53                 if (pActor1 != null && pActor0 instanceof Rock && pActor1.getColor().equals(Color.black))
 54                 {
 55                     valid.add(new Integer(direction[i]));
 56                 }
 57             }
 58         }
 59         return valid;
 60     }
 61 
 62     public boolean canMove()
 63     {
 64         if (this.isContinuing == true)
 65         {
 66             this.isContinuing = false;
 67             this.next = this.getLocation().getAdjacentLocation(this.getDirection());
 68             if (this.isBacktracing)
 69             {
 70                   this.pathLocation.pop();
 71             }
 72             return true;
 73         }
 74       
 75         ArrayList directions = this.getValid(this.getLocation());
 76         if (directions.size() >= 1)
 77         {
 78             this.isContinuing = true;
 79             this.isBacktracing = false;
 80             Random rand = new Random();
 81             int randNum = rand.nextInt(directions.size());
 82             int nextDir = ((Integer)directions.get(randNum)).intValue();
 83             next = this.getLocation().getAdjacentLocation(nextDir);
 84 
 85         }
 86         else if (!this.pathLocation.empty())
 87         {
 88             this.isContinuing = true;
 89             this.isBacktracing = true;
 90             this.next = (Location)this.pathLocation.pop();
 91         }
 92         else
 93         {
 94             this.isEnd = true;
 95             return false;
 96         }
 97         return true;
 98     }
 99 
100     public void move()
101     {
102         Grid gr = getGrid();
103         if (gr == null)
104             return;
105         Location loc = getLocation();
106         if (gr.isValid(this.next)) 
107         {
108             setDirection(getLocation().getDirectionToward(this.next));
109             moveTo(this.next);
110             if (!this.isBacktracing)
111                 this.pathLocation.push(loc);
112         }
113         else 
114         {
115             removeSelfFromGrid();
116         }
117     }
118 }

弱弱地说一句,wiki上给了一个挖迷宫的jar包,可以去下载一下然后找些工具打开..因为java是翻译成字节码,然后再由jvm解释,所以想从class转成java是非常容易的。

 

 

第三天:

  拼图部分很无聊,就是让我们写广搜和启发式搜索的估值函数,而且是照着伪代码去写。答辩的时候,我跟TA建议,这部分就不要给代码,直接给文档,里面说明一下各种数据结构及设计,然后由我们自己去编码。不要让我们在别人写的半个类中改代码,并且运用到别人写的另外的类。这样不小心就非常蛋疼。

  直接上代码吧:

  1 package jigsaw;
  2 
  3 import java.io.FileWriter;
  4 import java.io.IOException;
  5 import java.io.PrintWriter;
  6 import java.util.Iterator;
  7 import java.util.Vector;
  8 
  9 /** 在此类中填充算法,完成重拼图游戏(N-数码问题)
 10  * @author abe
 11  *
 12  */
 13 public class Jigsaw {
 14     JigsawNode beginJNode;        // 拼图的起始状态节点
 15     JigsawNode endJNode;        // 拼图的目标状态节点
 16     JigsawNode currentJNode;    // 拼图的当前状态节点
 17     private Vector<JigsawNode> openList;    // open表 :用以保存已发现但未访问的节点
 18     private Vector<JigsawNode> closeList;    // close表:用以保存已访问的节点
 19     private Vector<JigsawNode> solutionPath;// 解路径  :用以保存从起始状态到达目标状态的移动路径中的每一个状态节点
 20     private boolean isCompleted;    // 完成标记:初始为false;当求解成功时,将该标记至为true
 21     private int searchedNodesNum;    // 已访问节点数: 用以记录所有访问过的节点的数量
 22 
 23     /**拼图构造函数
 24      * @param bNode - 初始状态节点
 25      * @param eNode - 目标状态节点
 26      */
 27     public Jigsaw(JigsawNode bNode, JigsawNode eNode) {
 28         this.beginJNode = new JigsawNode(bNode);
 29         this.endJNode = new JigsawNode(eNode);
 30         this.currentJNode = new JigsawNode(bNode);
 31         this.openList = new Vector<JigsawNode>();
 32         this.closeList = new Vector<JigsawNode>();
 33         this.solutionPath = null;
 34         this.isCompleted = false;
 35         this.searchedNodesNum = 0;
 36     }
 37 
 38     /**此函数用于打散拼图:将输入的初始状态节点jNode随机移动len步,返回其打散后的状态节点
 39      * @param jNode - 初始状态节点
 40      * @param len - 随机移动的步数
 41      * @return 打散后的状态节点
 42      */
 43     public static JigsawNode scatter(JigsawNode jNode, int len) {
 44         int randomDirection;
 45         len += (int) (Math.random() * 2);
 46         JigsawNode jigsawNode = new JigsawNode(jNode);
 47         for (int t = 0; t < len; t++) {
 48             int[] movable = jigsawNode.canMove();
 49             do{randomDirection = (int) (Math.random() * 4);}
 50             while(0 == movable[randomDirection]);
 51             jigsawNode.move(randomDirection);
 52         }
 53         jigsawNode.setInitial();
 54         return jigsawNode;
 55     }
 56 
 57     /**获取拼图的当前状态节点
 58      * @return currentJNode -  拼图的当前状态节点
 59      */
 60     public JigsawNode getCurrentJNode() {
 61         return currentJNode;
 62     }
 63 
 64     /**设置拼图的初始状态节点
 65      * @param jNode - 拼图的初始状态节点
 66      */
 67     public void setBeginJNode(JigsawNode jNode) {
 68         beginJNode = jNode;
 69     }
 70 
 71     /**获取拼图的初始状态节点
 72      * @return beginJNode - 拼图的初始状态节点
 73      */
 74     public JigsawNode getBeginJNode() {
 75         return beginJNode;
 76     }
 77 
 78     /**设置拼图的目标状态节点
 79      * @param jNode - 拼图的目标状态节点
 80      */
 81     public void setEndJNode(JigsawNode jNode) {
 82         this.endJNode = jNode;
 83     }
 84 
 85     /**获取拼图的目标状态节点
 86      * @return endJNode - 拼图的目标状态节点
 87      */
 88     public JigsawNode getEndJNode() {
 89         return endJNode;
 90     }
 91 
 92     /**获取拼图的求解状态
 93      * @return isCompleted - 拼图已解为true;拼图未解为false
 94      */
 95     public boolean isCompleted() {
 96         return isCompleted;
 97     }
 98 
 99     /**计算解的路劲
100      * @return 若有解,则将结果保存在solutionPath中,返回true; 若无解,则返回false
101      */
102     private boolean calSolutionPath() {
103         if (!this.isCompleted()) {
104             return false;
105         } else {
106             JigsawNode jNode = this.currentJNode;
107             solutionPath = new Vector<JigsawNode>();
108             while (jNode != null) {
109                 solutionPath.addElement(jNode);
110                 jNode = jNode.getParent();
111             }
112             return true;
113         }
114     }
115 
116     /**获取解路径文本
117      * @return 解路径solutionPath的字符串,若有解,则分行记录从初始状态到达目标状态的移动路径中的每一个状态节点;
118      * 若未解或无解,则返回提示信息。
119      */
120     public String getSolutionPath() {
121         String str = new String();
122         str += "Begin->";
123         if (this.isCompleted) {
124             for (int i = solutionPath.size()-1; i>=0; i--) {
125                 str += solutionPath.elementAt(i).toString() + "->";
126             }
127             str+="End";
128         } else
129             str = "Jigsaw Not Completed.";
130         return str;
131     }
132 
133     /**获取访问过的节点数searchedNodesNum
134      * @return 返回所有已访问过的节点总数
135      */
136     public int getSearchedNodesNum() {
137         return searchedNodesNum;
138     }
139     
140     /**将搜索结果写入文件中,同时显示在控制台
141      * 若搜索失败,则提示问题无解,输出已访问节点数;
142      * 若搜索成功,则输出初始状态beginJnode,目标状态endJNode,已访问节点数searchedNodesNum,路径深度nodeDepth和解路径solutionPath。
143      * @param pw - 文件输出PrintWriter类对象,如果pw为null,则写入到D://Result.txt
144      * @throws IOException
145      */
146     public void printResult(PrintWriter pw) throws IOException{
147         boolean flag = false;
148         if(pw == null){
149             pw = new PrintWriter(new FileWriter("Result.txt"));// 将搜索过程写入D://BFSearchDialog.txt
150             flag = true;
151         }
152         if (this.isCompleted == true) {
153             // 写入文件
154             pw.println("Jigsaw Completed");
155             pw.println("Begin state:" + this.getBeginJNode().toString());
156             pw.println("End state:" + this.getEndJNode().toString());
157             pw.println("Solution Path: ");
158             pw.println(this.getSolutionPath());
159             pw.println("Total number of searched nodes:" + this.getSearchedNodesNum());
160             pw.println("Length of the solution path is:" + this.getCurrentJNode().getNodeDepth());
161 
162             
163             // 输出到控制台
164             System.out.println("Jigsaw Completed");
165             System.out.println("Begin state:" + this.getBeginJNode().toString());
166             System.out.println("End state:" + this.getEndJNode().toString());
167             System.out.println("Solution Path: ");
168             System.out.println(this.getSolutionPath());
169             System.out.println("Total number of searched nodes:" + this.getSearchedNodesNum());
170             System.out.println("Length of the solution path is:" + this.getCurrentJNode().getNodeDepth());
171 
172             
173         } 
174         else {
175             // 写入文件
176             pw.println("No solution. Jigsaw Not Completed");
177             pw.println("Begin state:" + this.getBeginJNode().toString());
178             pw.println("End state:" + this.getEndJNode().toString());
179             pw.println("Total number of searched nodes:"
180                     + this.getSearchedNodesNum());
181             
182             // 输出到控制台
183             System.out.println("No solution. Jigsaw Not Completed");
184             System.out.println("Begin state:" + this.getBeginJNode().toString());
185             System.out.println("End state:" + this.getEndJNode().toString());
186             System.out.println("Total number of searched nodes:"
187                     + this.getSearchedNodesNum());
188         }
189         if(flag)
190             pw.close();
191     }
192 
193     /**探索所有与jNode邻接(上、下、左、右)且未曾被访问的节点
194      * @param jNode - 要探索的节点
195      * @return 包含所有与jNode邻接且未曾被访问的节点的Vector<JigsawNode>对象
196      */
197     private Vector<JigsawNode> findFollowJNodes(JigsawNode jNode) {
198         Vector<JigsawNode> followJNodes = new Vector<JigsawNode>();
199         JigsawNode tempJNode;
200         for(int i=0; i<4; i++){
201             tempJNode = new JigsawNode(jNode);
202             if(tempJNode.move(i) && !this.closeList.contains(tempJNode) && !this.openList.contains(tempJNode))
203                 followJNodes.addElement(tempJNode);
204         }
205         return followJNodes;
206     }
207 
208     /**排序插入openList:按照节点的代价估值(estimatedValue)将节点插入openList中,估值小的靠前。
209      * @param jNode - 要插入的状态节点
210      */
211     private void sortedInsertOpenList(JigsawNode jNode) {
212         this.estimateValue(jNode);
213         for (int i = 0; i < this.openList.size(); i++) {
214             if (jNode.getEstimatedValue() < this.openList.elementAt(i)
215                     .getEstimatedValue()) {
216                 this.openList.insertElementAt(jNode, i);
217                 return;
218             }
219         }
220         this.openList.addElement(jNode);
221     }
222     
223     
224     
225     // ****************************************************************
226     // *************************实验任务************************
227     /**实验任务一:广度优先搜索算法,求指定3*3拼图(8-数码问题)的最优解
228      * 要求:填充广度优先搜索算法BFSearch(),执行测试脚本RunnerPart1
229      * 主要涉及函数:BFSearch()
230      */
231     /**实验任务二:启发式搜索算法,求解随机5*5拼图(24-数码问题)
232      * 要求:1.修改启发式搜索算法ASearch()和代价估计函数estimateValue(),执行测试脚本RunnerPart2
233      *      2.访问节点总数不超过25000个
234      * 主要涉及函数:ASearch(),estimateValue()
235      */
236     // ****************************************************************
237     
238     /**(实验一)广度优先搜索算法,求解指定3*3拼图(8-数码问题)的最优解。
239      * 要求函数结束后:1,isCompleted记录了求解完成状态;
240      *              2,closeList记录了所有访问过的节点;
241      *                 3,searchedNodesNum记录了访问过的节点数;
242      *              4,solutionPath记录了解路径。
243      * @return isCompleted, 搜索成功时为true,失败为false
244      * @throws IOException
245      */
246     public boolean BFSearch() throws IOException {
247         // 将搜索过程写入D://BFSearchDialog.txt
248         String filePath = "BFSearchDialog.txt";
249         PrintWriter pw = new PrintWriter(new FileWriter(filePath));
250         // *************************************
251 
252         // Write your code here.
253         //(1)将起始节点放入一个open列表中。
254         this.openList.add(this.beginJNode);
255 
256         //(2)如果open列表为空,则搜索失败,问题无解;否则重复以下步骤:
257         while (openList.size() > 0)
258         {
259             // 1.访问open列表中的第一个节点v,若v为目标节点,则搜索成功,退出。
260             this.currentJNode = openList.firstElement();
261             if (currentJNode.equals(this.endJNode))
262             {
263                 this.isCompleted = true;
264                 this.calSolutionPath();
265                 break;
266             }
267             
268             // 2.从open列表中删除节点v,放入close列表中。
269             this.openList.removeElementAt(0);
270             closeList.add(currentJNode);
271             searchedNodesNum++;
272             
273             // 3.将所有与v邻接且未曾被访问的节点放入open列表中。
274             Vector<JigsawNode>v = this.findFollowJNodes(currentJNode);
275             int size = v.size();
276             for (int i = 0; i < size; i++)
277             {
278                 openList.add(v.get(i));
279             }
280         }
281         
282         // *************************************
283         this.printResult(pw);
284         pw.close();
285         System.out.println("Record into " + filePath);
286         return isCompleted;
287     }
288     
289     /**(Demo+实验二)启发式搜索。访问节点数大于30000个则认为搜索失败。
290      * 函数结束后:isCompleted记录了求解完成状态;
291      *           closeList记录了所有访问过的节点;
292      *           searchedNodesNum记录了访问过的节点数;
293      *           solutionPath记录了解路径。
294      *  搜索过程和结果会记录在D://DemoASearchDialog.txt中。
295      * @return 搜索成功返回true,失败返回false
296      * @throws IOException
297      */
298     
299     /*修改Jigsaw类中的启发式搜索算法ASearch()和代价估计函数estimateValue(),
300      * 对随机生成的5*5拼图初始状态进行求解。
301      * 具体要求:
302      * 1,修改Jigsaw类中的ASearch()和estimateValue()两个函数,可添加其他函数;
303      * 2,要求访问节点总数不超过25000个;
304      * 3,要求算法结束后:
305      *     1.isCompleted记录了求解完成状态;
306      *     2.closeList记录了所有访问过的节点;
307      *     3.searchedNodesNum记录了访问过的节点数;
308      *     4.solutionPath记录了解路径。
309      *     4,必须通过测试脚本RunnerPart2的测试检查。
310      *     注意:解此题前必须先把JigsawNode中的拼图维度dimension改为5。
311      *     5,进行多次测试(次数>=20),画出频率分布折线图,在文档中提交。如图6所示,
312      *     X轴表示步数(<5000, <10000, ... , <25000, >25000),Y轴为步数在该范围内的频率。
313      *     TA会对程序进行抽查,因此不要捏造数据或故意选取跑的好的结果。
314      * */
315     /*   估价函数f(n)用来估计节点n的重要性,表示为:从起始节点,经过节点n,到达目标节点的代价。
316      *   f(n)越小,表示节点n越优良,应该优先访问它的邻接节点。可参考的估价方法有:
317         1) 所有 放错位的数码 个数
318         2) 所有 放错位的数码与其正确位置的距离 之和
319         3) 后续节点不正确的数码个数
320         4) .....
321         可以同时使用多个估价方法,f(n) = a*f1(n) + b*f2(n) „„,通过适当调整权重(a、b、„„),
322         能够加快搜索速度。
323      * */
324     public boolean ASearch() throws IOException{
325         // 将搜索过程写入ASearchDialog.txt
326         String filePath = "ASearchDialog.txt";
327         PrintWriter pw = new PrintWriter(new FileWriter(filePath));
328         
329         // 访问节点数大于30000个则认为搜索失败
330         int maxNodesNum = 25000;  
331         
332         // 用以存放某一节点的邻接节点
333         Vector<JigsawNode> followJNodes = new Vector<JigsawNode>(); 
334         
335         // 重置求解完成标记为false
336         isCompleted = false;           
337         
338         // (1)将起始节点放入openList中
339         this.sortedInsertOpenList(this.beginJNode);
340         
341         // (2) 如果openList为空,或者访问节点数大于maxNodesNum个,则搜索失败,问题无解;否则循环直到求解成功
342         while (this.openList.isEmpty() != true && searchedNodesNum <= maxNodesNum) {
343             
344             // (2-1)访问openList的第一个节点N,置为当前节点currentJNode
345             //      若currentJNode为目标节点,则搜索成功,设置完成标记isCompleted为true,计算解路径,退出。
346             this.currentJNode = this.openList.elementAt(0);
347             if (this.currentJNode.equals(this.endJNode)){
348                 isCompleted = true;
349                 this.calSolutionPath();
350                 break;
351             }
352             
353             // (2-2)从openList中删除节点N,并将其放入closeList中,表示以访问节点            
354             this.openList.removeElementAt(0);
355             this.closeList.addElement(this.currentJNode);
356             searchedNodesNum++;
357             
358                 // 记录并显示搜索过程
359                 pw.println("Searching.....Number of searched nodes:" + this.closeList.size() + "   Current state:" + this.currentJNode.toString());
360                 System.out.println("Searching.....Number of searched nodes:" + this.closeList.size() + "   Current state:" + this.currentJNode.toString());            
361 
362             // (2-3)寻找所有与currentJNode邻接且未曾被访问的节点,将它们按代价估值从小到大排序插入openList中
363             followJNodes = this.findFollowJNodes(this.currentJNode);
364             while (!followJNodes.isEmpty()) {
365                 this.sortedInsertOpenList(followJNodes.elementAt(0));
366                 followJNodes.removeElementAt(0);
367             }
368         }
369         
370         this.printResult(pw);    // 记录搜索结果
371         pw.close();             // 关闭输出文件
372         System.out.println("Record into " + filePath);
373         return isCompleted;
374     }
375     
376     /**(Demo+实验二)
377      * @param jNode - 要计算代价估计值的节点;此函数会改变该节点的estimatedValue属性值。
378      */
379     private void estimateValue(JigsawNode jNode)
380     {
381         int s = 0 * getEstimateValue0(jNode) + 
382                 1 * getEstimateValue1(jNode) +
383                 1 * getEstimateValue2(jNode);
384         jNode.setEstimatedValue(s);
385     }
386     //所有 放错位的数码 个数
387     private int getEstimateValue0(JigsawNode jNode)
388     {
389         int s = 0;
390         int dimension = JigsawNode.getDimension();
391         int currentNodeState[] = jNode.getNodesState();
392         int endNodeState[] = endJNode.getNodesState();
393         for (int i = 1; i <= dimension * dimension; i++)
394         {
395             if (currentNodeState[i] != endNodeState[i])    
396                 s++;
397         }
398         return s;
399     }
400     //所有 放错位的数码与其正确位置的距离 之和
401     private int getEstimateValue1(JigsawNode jNode)
402     {
403         int s = 0;
404         int col0, row0, col1, row1;
405         
406         int dimension = JigsawNode.getDimension();
407         int currentNodeState[] = jNode.getNodesState();
408         int endNodeState[] = endJNode.getNodesState();
409         
410         for (int i = 1; i <= dimension * dimension; i++)
411         {
412             if (currentNodeState[i] != 0 && currentNodeState[i] != endNodeState[i])
413             {
414                 row0 = (int) (i - 1) / dimension;
415                 col0 = (int) (i + 4) % dimension;
416                 for (int j = 0; j <= dimension * dimension; j++)
417                 {
418                     if (currentNodeState[i] == endNodeState[j])
419                     {
420                         row1 = (int) (j - 1) / dimension;
421                         col1 = (int) (j + 4) % dimension;
422                         s += (Math.abs(row1 - row0) + Math.abs(col1 - col0));
423                         break;
424                     }
425                 }
426             }
427         }
428         return s;
429     }
430     // 后续节点不正确的数码个数
431     //计算并修改状态节点jNode的代价估计值:f(n)=s(n)。
432     // s(n)代表后续节点不正确的数码个数
433     private int getEstimateValue2(JigsawNode jNode)
434     {
435         int s = 0; 
436         int dimension = JigsawNode.getDimension();
437         for(int index =1 ; index<dimension*dimension; index++){
438             if(jNode.getNodesState()[index]+1!=jNode.getNodesState()[index+1])
439                 s++;
440         }
441         return s;
442     }
443 }
View Code

 

第四天:

  

  答辩呀,感谢我们有一个做得了学霸写得了代码,写得了文档做得了ppt的队友~

 

  有问题欢迎提问。看到这篇文章的各位,希望大家还是专心写点代码,不要总想着找一些可以参考的,这样会养成自己非常不严谨和不擅长思考的习惯,渐渐地就会变成菜鸟。前两部分有时间的话,记得多看一下WorkFrame,GUI,Grid这三大块(其实就只有数据和UI两块)。如果大家对写游戏有兴趣,欢迎联系我,发邮件给我。

 

posted @ 2013-07-12 16:51  gumcstronger  阅读(776)  评论(0)    收藏  举报