AI算法玩转象棋
毕加锁
于 2022-06-09 21:03:46 发布
135
收藏 11
文章标签: fpga开发 python
版权
大家好 我是毕加锁 (锁!)
今天教大家 python 象棋AI算法
一,棋子的着法
com.bylaw ={} 首先创建一个数组,用于存储该棋子处于某一点时所能走到着点
(1)车:
com.bylaw.c = function (x,y,map,my){
var d=[];
//左侧检索 若存在棋子且颜色不同则push过去并结束循环,否则一步步push
for (var i=x-1; i>= 0; i--){
if (mapy) {
if (com.mansmap[y].my!=my) d.push([i,y]);
break
}else{
d.push([i,y])
}
}
//右侧检索
for (var i=x+1; i <= 8; i++){
if (mapy) {
if (com.mansmap[y].my!=my) d.push([i,y]);
break
}else{
d.push([i,y])
}
}
//上检索
for (var i = y-1 ; i >= 0; i--){
if (mapi) {
if (com.mansmap[i].my!=my) d.push([x,i]);
break
}else{
d.push([x,i])
}
}
//下检索
for (var i = y+1 ; i<= 9; i++){
if (mapi) {
if (com.mansmap[i].my!=my) d.push([x,i]);
break
}else{
d.push([x,i])
}
}
return d;
}
算法分析:
分别向上,下,左,右四个方向搜索,若找到一个点且颜色与该棋子不同(敌对棋子),则将该点坐标记录在d数组中,若某一方向上没有其他棋子,将这一方向上所有坐标都记录在d数组中。简单来讲:就是将以车这个棋子为中心的十字上的坐标都记录在d数组中(你早这样说多好~,开始说那么多)
前提补充:
1,代码中的map:
com.initMap = [
['C0','M0','X0','S0','J0','S1','X1','M1','C1'],
[ , , , , , , , , ],
[ ,'P0', , , , , ,'P1', ],
['Z0', ,'Z1', ,'Z2', ,'Z3', ,'Z4'],
[ , , , , , , , , ],
[ , , , , , , , , ],
['z0', ,'z1', ,'z2', ,'z3', ,'z4'],
[ ,'p0', , , , , ,'p1', ],
[ , , , , , , , , ],
['c0','m0','x0','s0','j0','s1','x1','m1','c1']
];
这里的字符串代表每个棋子的key值:
com.keys = { //设定每类棋子的key值
"c0":"c","c1":"c",
"m0":"m","m1":"m",
"x0":"x","x1":"x",
"s0":"s","s1":"s",
"j0":"j",
"p0":"p","p1":"p",
"z0":"z","z1":"z","z2":"z","z3":"z","z4":"z","z5":"z",
"C0":"C","C1":"C",
"M0":"M","M1":"M",
"X0":"X","X1":"X",
"S0":"S","S1":"S",
"J0":"J",
"P0":"P","P1":"P",
"Z0":"Z","Z1":"Z","Z2":"Z","Z3":"Z","Z4":"Z","Z5":"Z",
}
2,my:
标记值:1代表红色方(这里指人。玩家永远操纵红色) ; -1代表AI
3,mapy与d.push(i)
左方向上搜索,y坐标不变,x坐标遍历,而体现在map当中(向上翻第一点),仔细看就会发现:第一个下标代表y值,第二个下标代表x值,其与坐标值正好相反
其他方向上以此类推。。。
(2)马
com.bylaw.m = function (x,y,map,my){
var d=[];
//1点钟方向 不绊马脚 1点不存在棋子或1点棋子颜色不同 push
if ( y-2>= 0 && x+1<= 8 && !play.mapy-1 &&(!com.mansmap[y-2] || com.mansmap[y-2].my!=my)) d.push([x+1,y-2]);
//2点
if ( y-1>= 0 && x+2<= 8 && !play.mapy &&(!com.mansmap[y-1] || com.mansmap[y-1].my!=my)) d.push([x+2,y-1]);
//4点
if ( y+1<= 9 && x+2<= 8 && !play.mapy &&(!com.mansmap[y+1] || com.mansmap[y+1].my!=my)) d.push([x+2,y+1]);
//5点
if ( y+2<= 9 && x+1<= 8 && !play.mapy+1 &&(!com.mansmap[y+2] || com.mansmap[y+2].my!=my)) d.push([x+1,y+2]);
//7点
if ( y+2<= 9 && x-1>= 0 && !play.mapy+1 &&(!com.mansmap[y+2] || com.mansmap[y+2].my!=my)) d.push([x-1,y+2]);
//8点
if ( y+1<= 9 && x-2>= 0 && !play.mapy &&(!com.mansmap[y+1] || com.mansmap[y+1].my!=my)) d.push([x-2,y+1]);
//10点
if ( y-1>= 0 && x-2>= 0 && !play.mapy &&(!com.mansmap[y-1] || com.mansmap[y-1].my!=my)) d.push([x-2,y-1]);
//11点
if ( y-2>= 0 && x-1>= 0 && !play.mapy-1 &&(!com.mansmap[y-2] || com.mansmap[y-2].my!=my)) d.push([x-1,y-2]);
return d;
}
算法分析:
当马处于一点时,可以走的最多情况有8种方向,分别讨论每个方向:如果不绊马脚,且该方向上那着点没有棋子或棋子颜色不同,则记录该着点
图例分析:
有点丑,用画图做的,不要在意这些细节
(三)相
com.bylaw.x = function (x,y,map,my){
var d=[];
if (my===1){ //红方 颜色不同,y的取值范围不同,且不能过河
//4点半 不绊象脚 4.5位置没子或棋子颜色不同 push
if ( y+2<= 9 && x+2<= 8 && !play.mapy+1 && (!com.mansmap[y+2] || com.mansmap[y+2].my!=my)) d.push([x+2,y+2]);
//7点半
if ( y+2<= 9 && x-2>= 0 && !play.mapy+1 && (!com.mansmap[y+2] || com.mansmap[y+2].my!=my)) d.push([x-2,y+2]);
//1点半
if ( y-2>= 5 && x+2<= 8 && !play.mapy-1 && (!com.mansmap[y-2] || com.mansmap[y-2].my!=my)) d.push([x+2,y-2]);
//10点半
if ( y-2>= 5 && x-2>= 0 && !play.mapy-1 && (!com.mansmap[y-2] || com.mansmap[y-2].my!=my)) d.push([x-2,y-2]);
}else{
//4点半
if ( y+2<= 4 && x+2<= 8 && !play.mapy+1 && (!com.mansmap[y+2] || com.mansmap[y+2].my!=my)) d.push([x+2,y+2]);
//7点半
if ( y+2<= 4 && x-2>= 0 && !play.mapy+1 && (!com.mansmap[y+2] || com.mansmap[y+2].my!=my)) d.push([x-2,y+2]);
//1点半
if ( y-2>= 0 && x+2<= 8 && !play.mapy-1 && (!com.mansmap[y-2] || com.mansmap[y-2].my!=my)) d.push([x+2,y-2]);
//10点半
if ( y-2>= 0 && x-2>= 0 && !play.mapy-1 && (!com.mansmap[y-2] || com.mansmap[y-2].my!=my)) d.push([x-2,y-2]);
}
return d;
}
算法分析:
因为相不能过河,所以要按颜色分情况讨论(不同颜色,y坐标不同)
而每种颜色的相都有四种可能着法,与马类似:如果不绊象脚, 着点没有棋子或棋子颜色不同,记录
图例分析:
(四)士
com.bylaw.s = function (x,y,map,my){
var d=[];
if (my===1){ //红方
//4点半
if ( y+1<= 9 && x+1<= 5 && (!com.mansmap[y+1] || com.mansmap[y+1].my!=my)) d.push([x+1,y+1]);
//7点半
if ( y+1<= 9 && x-1>= 3 && (!com.mansmap[y+1] || com.mansmap[y+1].my!=my)) d.push([x-1,y+1]);
//1点半
if ( y-1>= 7 && x+1<= 5 && (!com.mansmap[y-1] || com.mansmap[y-1].my!=my)) d.push([x+1,y-1]);
//10点半
if ( y-1>= 7 && x-1>= 3 && (!com.mansmap[y-1] || com.mansmap[y-1].my!=my)) d.push([x-1,y-1]);
}else{
//4点半
if ( y+1<= 2 && x+1<= 5 && (!com.mansmap[y+1] || com.mansmap[y+1].my!=my)) d.push([x+1,y+1]);
//7点半
if ( y+1<= 2 && x-1>= 3 && (!com.mansmap[y+1] || com.mansmap[y+1].my!=my)) d.push([x-1,y+1]);
//1点半
if ( y-1>= 0 && x+1<= 5 && (!com.mansmap[y-1] || com.mansmap[y-1].my!=my)) d.push([x+1,y-1]);
//10点半
if ( y-1>= 0 && x-1>= 3 && (!com.mansmap[y-1] || com.mansmap[y-1].my!=my)) d.push([x-1,y-1]);
}
return d;
}
算法分析:
士不能出九宫格,x,y值都有限制。按颜色分情况讨论。每种颜色各有4中可能着法:如果该着点没棋子或棋子颜色不同,记录
图例分析:
这个简单了,就不画图了~ ~ ~ ~
(五)将
com.bylaw.j = function (x,y,map,my){
var d=[];
var isNull=(function (y1,y2){
var y1=com.mans["j0"].y; //红帅的y
var x1=com.mans["J0"].x; //黑将的x
var y2=com.mans["J0"].y; //黑将的y
for (var i=y1-1; i>y2; i--){
if (mapi) return false; //将与将之间非空,有子
}
return true;
})();
if (my===1){ //红方
//下
if ( y+1<= 9 && (!com.mansmap[y+1] || com.mansmap[y+1].my!=my)) d.push([x,y+1]);
//上
if ( y-1>= 7 && (!com.mansmap[y-1] || com.mansmap[y-1].my!=my)) d.push([x,y-1]);
//老将对老将的情况
if ( com.mans["j0"].x == com.mans["J0"].x &&isNull) d.push([com.mans["J0"].x,com.mans["J0"].y]); //x相等,且中间为空,push黑将的坐标
}else{
//下
if ( y+1<= 2 && (!com.mansmap[y+1] || com.mansmap[y+1].my!=my)) d.push([x,y+1]);
//上
if ( y-1>= 0 && (!com.mansmap[y-1] || com.mansmap[y-1].my!=my)) d.push([x,y-1]);
//老将对老将的情况
if ( com.mans["j0"].x == com.mans["J0"].x &&isNull) d.push([com.mans["j0"].x,com.mans["j0"].y]); //push红帅的坐标
}
//右
if ( x+1<= 5 && (!com.mansmap[y] || com.mansmap[y].my!=my)) d.push([x+1,y]);
//左
if ( x-1>= 3 && (!com.mansmap[y] || com.mansmap[y].my!=my))d.push([x-1,y]);
return d;
}
算法分析:
将除了颜色不同导致y值不同外,还有种特殊情况:即老将见面。所以开始先写个函数,判断将与帅之间是否有其他棋子
接下来按颜色不同分情况讨论上下两种着法:重点 是y值的界定。以帅为例:帅在棋盘下方,y坐标只能取7,8,9.如果向下走,则取7,8,所以y值最大为8.上与其类似。而判断完着法之后还要判断是否老将见面的特殊情况:如果两者x坐标相等且中间没其他棋子,之间闪现过去抢人头~ ~ ~然后victory
(六),炮
com.bylaw.p = function (x,y,map,my){
var d=[];
//左侧检索
var n=0;
for (var i=x-1; i>= 0; i--){
if (mapy) { //碰到子
if (n==0){ //若是第一个子,不用管,跳出本次循环,标记位加1
n++;
continue;
}else{ //若不是第一个子,判断颜色若不同,push过去并结束循环
if (com.mansmap[y].my!=my) d.push([i,y]);
break
}
}else{ //若一直碰不到子,将子走到最左
if(n==0) d.push([i,y])
}
}
//右侧检索
var n=0;
for (var i=x+1; i <= 8; i++){
if (mapy) {
if (n==0){
n++;
continue;
}else{
if (com.mansmap[y].my!=my) d.push([i,y]);
break
}
}else{
if(n==0) d.push([i,y])
}
}
//上检索
var n=0;
for (var i = y-1 ; i >= 0; i--){
if (mapi) {
if (n==0){
n++;
continue;
}else{
if (com.mansmap[i].my!=my) d.push([x,i]);
break
}
}else{
if(n==0) d.push([x,i])
}
}
//下检索
var n=0;
for (var i = y+1 ; i<= 9; i++){
if (mapi) {
if (n==0){
n++;
continue;
}else{
if (com.mansmap[i].my!=my) d.push([x,i]);
break
}
}else{
if(n==0) d.push([x,i])
}
}
return d;
}