part2完成了Bug的四个拓展类CircleBug
、SpiralBug
、ZBug
、DancingBug,
CircleBug只是将Bug中的turn()由执行一次转化为执行两次。
DancingBug会一次读进一个数组,并且按照数组中的数值转动固定的方向并且行走,
当数组读取完毕时候重新回到数组首个数值重新开始,从而能够实现不停地跳舞。
ZBug是一个能走Z字型的Bug的拓展类,这个类实现比较容易,但要传入一个参数来决定Z的大小。
SpiralBug螺旋类,按照螺旋从中心向外拓展,最好在无界网格中运行,效果更好。
part3主要是实现一个jumper类拓展Actor类,并且做了相应的juint测试,这一部分可以自己规定自己的jumper类的动作行为,关键是juint的测试要写到位。
Part4主要实现 ModifiedChameleonCritter、ChameleonKid、RockHound、BlusterCritter、QuickCrab、KingCrab,这一部分是拓展一个crriter的小动物类,并且这些小动物分别具有不同的功能。具体实现也比较容易。
Part5主要是拓展Grid接口类,由于Grid接口有两个类,一个有界,一个无界。这一部分拓展了网格Grid类,并且实现了用哈希存储,动态数组拓展数组存储空间等功能。
第三周和第四周我们要实现一些拓展功能,包括ImageReader、N-Puzzle(华容道)和MazeBug(迷宫)三部分。
其中ImageReader要求实现二进制的读取图片并且实现色彩通道的一些改变从而实现图像的一些改变,还要能够利用java自带的API来写图片,还有写一个测试类。
其中主要的是在二进制的读取过程花费了很多时间,因为要理解这部分还是需要花很多功夫,毕竟之前都没有接触过。
其中读写重要部分的代码如下:
/* 保存位图信息
* 字节 #14-17 定义以下用来描述影像的区块(bitmapinfoheader)的大小。它的值是:40 - windows 3.2、95、nt、12 - os/2 1.x、240 - os/2 2.x
* 字节 #18-21 保存位图宽度(以像素个数表示)。
* 字节 #22-25 保存位图高度(以像素个数表示)。
* 字节 #26-27 保存所用彩色位面的个数。不经常使用。
* 字节 #28-29 保存每个像素的位数,它是图像的颜色深度。常用值是1、4、8(灰阶)和24(彩色)。
* 字节 #30-33 定义所用的压缩算法。允许的值是0、1、2、3、4、5。
* 0 - 没有压缩(也用bi_rgb表示)
* 1 - 行程长度编码 8位/像素(也用bi_rle8表示)
* 2 - 行程长度编码4位/像素(也用bi_rle4表示)
* 3 - bit field(也用bi_bitfields表示)
* 4 - jpeg图像(也用bi_jpeg表示)
* 5 - png图像(也用bi_png表示)
* 然而,由于大多数位图文件都是不压缩的,所以最常用的值是0。
* 字节 #34-37 保存图像大小。这是原始(:en:raw)位图数据的大小,不要与文件大小混淆。
* 字节 #38-41 保存图像水平方向分辨率。
* 字节 #42-45 保存图像竖值方向分辨率。
* 字节 #46-49 保存所用颜色数目。
* 字节 #50-53 保存所用重要颜色数目。当每个颜色都重要时这个值与颜色数目相等。
**/
public class myImageIO implements IImageIO{
private static final int FILEHEADER = 14;
private static final int INFOHEADER = 40;
private static final int FOUR_BYTE = 4;
private static final int MULTICOLOUR = 24;
private static final int GRAY = 8;
private static final int TWOFOUR = 24;
private static final int EIGHT = 8;
private static final int SIXTEEN = 16;
private static final int ZERO = 0;
private int bitCount;
public Image myRead(String filePath) throws IOException {
try {
FileInputStream file = new FileInputStream(filePath);
byte bHead[] = new byte[FILEHEADER];
byte bInfo[] = new byte[INFOHEADER];
int width = 0 , height = 0, size = 0;
//读取位图头
file.read(bHead, ZERO, FILEHEADER);
//读取位头信息
file.read(bInfo, ZERO, INFOHEADER);
// 图像宽度(像素点)
width = ((bInfo[7] << TWOFOUR) & 0xff000000)|
((bInfo[6] << SIXTEEN) & 0x00ff0000)|
((bInfo[5] << EIGHT) & 0x0000ff00)|
bInfo[4] & 0x000000ff;
// 图像高度(像素点)
height = ((bInfo[11] << TWOFOUR) & 0xff000000)|
((bInfo[10] << SIXTEEN) & 0x00ff0000)|
((bInfo[9] << EIGHT) & 0x0000ff00)|
bInfo[EIGHT] & 0x000000ff;
// 图像位数1 4 8 24
bitCount = ((bInfo[15] << EIGHT) & 0x0000ff00) | (bInfo[14] & 0x000000ff);
// 图像大小。原始(:en:raw)位图数据的大小,不要与文件大小混淆
size = ((bInfo[23] << TWOFOUR) & 0xff000000)|
((bInfo[22] << SIXTEEN) & 0x00ff0000)|
((bInfo[21] << EIGHT) & 0x0000ff00)|
bInfo[20] & 0x000000ff;
int pixelSize =0;
int npad = 0;
byte originalRGB[];
int RGBDate[];
Image image = null;
if (bitCount == MULTICOLOUR) {
// 计算空字节
npad = (size / height) - width * 3;
if (npad == FOUR_BYTE) {
npad = ZERO;
}
// 计算 pixel 大小
pixelSize = (width + npad) * 3 * height;
if (npad != ZERO) {
originalRGB = new byte[pixelSize];
} else {
originalRGB = new byte[size];
}
// 读取所有RGB数据
file.read(originalRGB, ZERO, pixelSize);
RGBDate = new int[height * width];
int index = 0;
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++) {
RGBDate[width * (height - j - 1) + i] =
(255 & 0xff) << TWOFOUR
| (((int)originalRGB[index + 2] & 0xff) << SIXTEEN)
| (((int)originalRGB[index + 1] & 0xff) << EIGHT)
| (int)originalRGB[index] & 0xff;
index += 3;
}
index += npad;
}
image = Toolkit.getDefaultToolkit().createImage(new MemoryImageSource(width, height, RGBDate, 0, width));
}
if (bitCount == GRAY) {
// 计算空字节
npad = (size / height) - width;
if (npad == FOUR_BYTE) {
npad = ZERO;
}
// 计算 pixel 大小
pixelSize = (width + npad) * height;
if (npad != ZERO) {
originalRGB = new byte[pixelSize];
} else {
originalRGB = new byte[size];
}
originalRGB = new byte[pixelSize];
// 读取所有RGB数据
file.read(originalRGB, ZERO, pixelSize);
RGBDate = new int[height * width];
int index =ZERO;
for (int j = ZERO; j < height; j++) {
for (int i = ZERO; i < width; i++) {
if (index >= pixelSize){
index = ZERO;
}
RGBDate[width * (height - j - 1) + i] =
(255 & 0xff) << TWOFOUR
| (((int)originalRGB[index] & 0xff) << SIXTEEN)
| (((int)originalRGB[index] & 0xff) << EIGHT)
| (int)originalRGB[index] & 0xff;
index += 1;
}
index += npad;
}
image = Toolkit.getDefaultToolkit().createImage(new MemoryImageSource(width, height, RGBDate, 0, width));
}
file.close();
return image;
} catch (Exception e) {
;
}
return (Image)null;
}
public Image myWrite(Image image, String file) throws IOException {
try {
int height = image.getHeight(null);
int width = image.getWidth(null);
int fileType;
if (bitCount == MULTICOLOUR){
fileType = BufferedImage.TYPE_3BYTE_BGR;
}
else{
fileType = BufferedImage.TYPE_BYTE_GRAY;
}
// 创建图片
BufferedImage bi = new BufferedImage(width, height, fileType);
bi.getGraphics().drawImage(image, 0, 0, null);
// 打开文件
File iFile= new File(file + ".bmp");
ImageIO.write(bi, "bmp", iFile);
} catch (Exception e) {
;
}
return image;
}
}
MazeBug类要求我们实现一个能用深度优先搜索的Bug类能够自动的寻找迷宫的寻找路径,并且在拓展部分增加方向参数能够选择更大可能性的方向行走。
其中最重要的是一个结构的理解,
public Stack> crossLocation = new Stack>();
我本来是觉得应该使用Stack
还有一个最后的内容就是实现华容道,这里要求我们实现用广度优先搜索和A*算法来减少对不要要结点的访问,从而实现更快的找到目标节点,更少的访问不必要的结点。其中要求我们写的程序能够通过简单的测试:test.sh。
这部分的代码如下:(我已经尽自己最大努力将无关结点的访问量降到最低了)
/**
* 在此类中填充算法,完成重拼图游戏(N-数码问题)
*/
public class Solution extends Jigsaw {
private Queue exploreList; // 用以保存已发现但未访问的节点
private Set visitedList; // 用以保存已发现的节点
private List solutionPath;// 解路径:用以保存从起始状态到达目标状态的移动路径中的每一个状态节点
private int searchedNodesNum;
/**
* 拼图构造函数
*/
public Solution() {
}
/**
* 拼图构造函数
* @param bNode - 初始状态节点
* @param eNode - 目标状态节点
*/
public Solution(JigsawNode bNode, JigsawNode eNode) {
super(bNode, eNode);
}
/**
*(实验一)广度优先搜索算法,求指定5*5拼图(24-数码问题)的最优解
* 填充此函数,可在Solution类中添加其他函数,属性
* @param bNode - 初始状态节点
* @param eNode - 目标状态节点
* @return 搜索成功时为true,失败为false
*/
public boolean BFSearch(JigsawNode bNode, JigsawNode eNode) {
/**
* 初始化一些数值,
* 选择具体的queue类型和hashset
*/
searchedNodesNum = 0;
solutionPath = null;
beginJNode = new JigsawNode(bNode);
endJNode = new JigsawNode(eNode);
currentJNode = new JigsawNode(bNode);
exploreList = new LinkedList<>();
visitedList = new HashSet<>();
int flag = 0;
/**
* 将第一个节点压入队列
*/
exploreList.add(beginJNode);
/**
* 如果exploreLis列表为空,则搜索失败,问题无解;否则重复以下步骤:
* a. 访问exploreLis列表中的第一个节点v,若v为目标节点,则搜索成功,退出。
* b. 从exploreLis列表中删除节点v,放入visitedList列表中。
* c. 将所有与v邻接且未曾被访问的节点放入exploreLis列表中。
*/
while (!exploreList.isEmpty()) {
//计算访问的节点数,节点数超过29000将放弃
searchedNodesNum++;
if(searchedNodesNum>=29000){
flag = 1;
break;
}
//访问结点
currentJNode = exploreList.poll();
//找到了结点就退出
if (currentJNode.equals(endJNode)) {
getPath();
break;
}
JigsawNode[] nextNodes = new JigsawNode[] {
new JigsawNode(currentJNode), new JigsawNode(currentJNode),
new JigsawNode(currentJNode), new JigsawNode(currentJNode)
};
/**
* 邻接点并且未访问的结点
*/
for (int i = 0; i < 4; i++) {
if (nextNodes[i].move(i) && !visitedList.contains(nextNodes[i])) {
exploreList.add(nextNodes[i]);
visitedList.add(nextNodes[i]);
}
}
}
if(flag == 1){
System.out.println("Jigsaw BFSearch Result:");
System.out.println("Begin state:" + this.getBeginJNode().toString());
System.out.println("End state:" + this.getEndJNode().toString());
System.out.println("The number of node is more than 29000! ");
}else{
System.out.println("Jigsaw BFSearch Result:");
System.out.println("Begin state:" + this.getBeginJNode().toString());
System.out.println("End state:" + this.getEndJNode().toString());
// System.out.println("Solution Path: ");
// System.out.println(this.getSolutionPath());
System.out.println("Total number of searched nodes:" + searchedNodesNum);
System.out.println("Depth of the current node is:" + this.getCurrentJNode().getNodeDepth());
}
return this.isCompleted();
}
/**
*(Demo+实验二)计算并修改状态节点jNode的代价估计值:f(n)
* 如 f(n) = s(n). s(n)代表后续节点不正确的数码个数
* 此函数会改变该节点的estimatedValue属性值
* 修改此函数,可在Solution类中添加其他函数,属性
* @param jNode - 要计算代价估计值的节点
*/
public void estimateValue(JigsawNode jNode) {
int s = 0; // 后续节点不正确的数码个数
int wrong_num = 0; // 放错位的数码个数
int mafuman_Distance = 0; // 曼哈顿距离
//int squre_Distance = 0;
int dimension = JigsawNode.getDimension(); //我们测试的默认维数是5
//注意第一个是空白格的位置,最后一个理论上是0,如果已经完成的话
for(int index = 1; index < dimension*dimension; index++){
if(jNode.getNodesState()[index] + 1 != jNode.getNodesState()[index+1])
s++;
}
//判断放错位置的结点
for(int index = 1; index <= dimension*dimension; index++){
if(jNode.getNodesState()[index] != index)
wrong_num++;
}
//判断距离,这里使用哈夫曼距离
for(int index = 1; index <= dimension*dimension; index++){
int realNum = jNode.getNodesState()[index];
if (realNum != index && realNum != 0) {
int realX = (realNum - 1) % dimension;
int realY = (realNum - 1) / dimension;
int indexX = (index - 1) % dimension;
int indexY = (index - 1) / dimension;
int distance = Math.abs(realX - indexX) + Math.abs(realY - indexY);
//double distance1 = Math.sqrt(Math.pow(realX - indexX, 2) + Math.pow(realY - indexY , 2));
mafuman_Distance += distance;
//squre_Distance += distance1;
}
}
jNode.setEstimatedValue( 15 * s + wrong_num + 10 * mafuman_Distance + jNode.getNodeDepth());
return ;
}
}
最后的一点总结和心得体会:这次实训还是学到了蛮多东西的,对于linux上使用也更加熟悉了一点,加深了对深度优先算法和广度优先算法的理解,短期时间学会了一门编程语言java,学会简单实用ant进行简单的编译运行打包等功能,学会用juint对类功能进行测试,学会使用sonar对代码语言风格进行了匡正和规范。总而言之,对于需要知识的我们是很有用的。当然这四周很累,还是希望四周结束之后能够给自己放一个小假,我想出去踏青了。最后的最后,希望大家都能享受愉快的生活!
中山大学数据科学与计算机学院
软件工程大二学生的实训总结报告
2018年5月5日19点56分