以前自己写过搜索华容道的最优解程序,想起来真是效率感人>﹏<,虽然如此,但是全是自己独立完成的,心里还是很高兴的,现在在微信小程序中看到了华容道,决定要再写一次华容道的程序。
这次看了网上的华容道程序,修改自许剑伟先生(福建莆田第十中学)的华容道程序(又是C语言,又是不友好的变量命名,看得我好辛苦≡(▔﹏▔)≡)
程序支持棋子三种移动类型
- 同一颗棋子为一步
- 直线为一步
- 一格为一步
盘面分析
这一步是将图片转为数组盘面,以前是采用取特定的点的颜色,确定棋子的类型,效果不是很好,这次使用的是图片相识算法,用了最简单的图片感知算法,效果非常好,也是因为图片比较简单的关系
基本样本
基本样本来源,包含了所有的横、竖棋子
private final static String[][] HASH_CODE = {
// 兵
{"3878EEDACB8BBEA1", "387CCEDACA8BBEA1", "387CCADACA8BBEA1", "387CEADACA8BBEA1", "3878D6DACBC9BE81",
"3858D2DACBC9BE81", "3878D6DACB89BEA1", "387CD2DACBC9BEA1"
},
// 横将
{"F9FAFB71238283C1", "030200E1F7F5F3FB", "0F0C1199E3E2F7E7", "F7F2F9F8FB720B09", "F9F9FBF3E3E1E0C0"},
// 竖将
{"C793BB9CB05C2E13", "7F7FFBB5980182AE", "EFF72707278DCDDB", "6EB79B075842073B", "DB91ADA1986E7E2A"},
// 曹操
{"E082810E04008FCF"}
};
示例
↓
10, 1, 5, 5,
10, 15, 15, 0,
11, 15, 15, 12,
11, 0, 13, 12,
2, 3, 13, 4
感知哈希算法
1. 缩小图片尺寸
这一步的作用是去除各种图片尺寸和图片比例的差异, 只保留结构、明暗等基本信息
2.转为灰度图片
简化色彩
1,2步直接使用下面代码完成
BufferedImage part = new BufferedImage(8, 8, BufferedImage.TYPE_BYTE_GRAY);
3.计算平均值
计算所有像素的灰度平均值
/**
* 计算数组平均值
* @param rgb
* @return
*/
public static double average(int[] rgb) {
double sum = 0;
for (int i = 0; i < rgb.length; i++) {
sum += rgb[i];
}
return sum / rgb.length;
}
4.比较像素的灰度
将每个像素的灰度,与平均值进行比较。大于或等于平均值,记为1;小于平均值,记为0
private static int[] rgbToBinariesHashCode(int[] rgb) {
int[] hashCode = new int[rgb.length];
double average = KlotskiNodeUtil.average(rgb);
for (int i = 0; i < rgb.length; i++) {
if (rgb[i] >= average) {
hashCode[i] = 1;
}
}
return hashCode;
}
/**
* 二进制数组转16进制字符
* @param binaries
* @return
*/
public static String binariesToHex(int[] binaries) {
char[] hashCode = new char[binaries.length >> 2];
for (int i = 0, j = 0; i < binaries.length; i += 4, j++) {
int sum = (binaries[i] << 3) + (binaries[i + 1] << 2) + (binaries[i + 2] << 1) + binaries[i + 3];
switch (sum) {
case 0:
hashCode[j] = '0';
break;
case 1:
hashCode[j] = '1';
break;
case 2:
hashCode[j] = '2';
break;
case 3:
hashCode[j] = '3';
break;
case 4:
hashCode[j] = '4';
break;
case 5:
hashCode[j] = '5';
break;
case 6:
hashCode[j] = '6';
break;
case 7:
hashCode[j] = '7';
break;
case 8:
hashCode[j] = '8';
break;
case 9:
hashCode[j] = '9';
break;
case 10:
hashCode[j] = 'A';
break;
case 11:
hashCode[j] = 'B';
break;
case 12:
hashCode[j] = 'C';
break;
case 13:
hashCode[j] = 'D';
break;
case 14:
hashCode[j] = 'E';
break;
case 15:
hashCode[j] = 'F';
break;
default:
}
}
return String.valueOf(hashCode);
}
5.计算hash值
将上一步的比较结果,组合在一起,就构成了一个64位的整数(转16进制),这就是这张图片的指纹。组合的次序并不重要,只要保证所有图片都采用同样次序就行了
得到指纹以后,就可以对比不同的图片,看看64位中有多少位是不一样的。在理论上,这等同于计算”汉明距离”(Hamming distance)。如果不相同的数据位不超过5,就说明两张图片很相似;如果大于10,就说明这是两张不同的图片
private final static char[] INDEX_TO_CHAR_TYPE = {KlotskiNodeUtil.E, KlotskiNodeUtil.S,
KlotskiNodeUtil.H, KlotskiNodeUtil.V, KlotskiNodeUtil.T};
/**
* 汉明距离
* @param hashCode
* @return
*/
private static char hanMingDifference(String hashCode) {
int difference = 16, index = 0;
for (int i = 0; i < HASH_CODE.length; i++) {
for (int j = 0; j < HASH_CODE[i].length; j++) {
int count = 0;
for (int k = 0; k < HASH_CODE[i][j].length(); k++) {
if (HASH_CODE[i][j].charAt(k) != hashCode.charAt(k)) {
count++;
}
}
// 取最近接近的棋子类型
if (count < 10 && count < difference) {
index = i + 1;
difference = count;
}
}
}
return INDEX_TO_CHAR_TYPE[index];
}