专注收集记录技术开发学习笔记、技术难点、解决方案
网站信息搜索 >> 请输入关键词:
您当前的位置: 首页 > 人工智能

Html5+javascript中国象棋 制造过程中用到的一些AI算法

发布时间:2011-07-03 09:15:15 文章来源:www.iduyao.cn 采编人员:星星草
Html5+javascript中国象棋 制作过程中用到的一些AI算法
心烦意乱睡不着,随便写点教程吧,不知道这类东西发哪个板块比较合适,先发这吧,哪位管理大大看着不顺眼再移吧。

声明一下啊,本人觉得这个不适合新手看,本人表达能力有限,别把你给误导喽,罪过啊。

象棋的预览地址:http://www.jnzo.com/chess/
代码未压缩,注释写的很清楚了,有兴趣的朋友可以一起改善
 

制作之前网上搜了一圈资料,关于中国象棋的还真少,不过倒是找到了国际象棋的资料,让我很钦佩的国际同行的专业精神,一个小小的象棋游戏,人家制定一系列标准,还组建了协会,开发了几种不同语言的引擎(可惜没有javascript的),佩服的五体投地。

制作过程先不说了,今儿就说说AI吧,有兴趣想学习html5制作过程的话,请留言,如果需要的话,后面补上。

————————————

言归正传,说到算法,可能许多程序员都会先皱眉头,其实那东西没有那么难,不是数学家也能写算法。

先说说人机对战AI的原理吧

树搜索

其实很简单
 

1、取得走法很简单,根据规则一步步的去算每个棋子哪个点能走,源文件里面有个 com.bylaw ,他下面是取得每种棋子走点的方法;
//棋子能走的着点
com.bylaw ={}
//车
com.bylaw.c = function (x,y,map,my){
        var d=[];
        //左侧检索
        for (var i=x-1; i>= 0; i--){
                if (map[y][i]) {
                        if (com.mans[map[y][i]].my!=my) d.push([i,y]);
                        break
                }else{
                        d.push([i,y])       
                }
        }
        //右侧检索
        for (var i=x+1; i <= 8; i++){
                if (map[y][i]) {
                        if (com.mans[map[y][i]].my!=my) d.push([i,y]);
                        break
                }else{
                        d.push([i,y])       
                }
        }
        //上检索
        for (var i = y-1 ; i >= 0; i--){
                if (map[i][x]) {
                        if (com.mans[map[i][x]].my!=my) d.push([x,i]);
                        break
                }else{
                        d.push([x,i])       
                }
        }
        //下检索
        for (var i = y+1 ; i<= 9; i++){
                if (map[i][x]) {
                        if (com.mans[map[i][x]].my!=my) d.push([x,i]);
                        break
                }else{
                        d.push([x,i])       
                }
        }
        return d;
}

//马
com.bylaw.m = function (x,y,map,my){
        var d=[];
                //1点
                if ( y-2>= 0 && x+1<= 8 && !play.map[y-1][x] &&(!com.mans[map[y-2][x+1]] || com.mans[map[y-2][x+1]].my!=my)) d.push([x+1,y-2]);
                //2点
                if ( y-1>= 0 && x+2<= 8 && !play.map[y][x+1] &&(!com.mans[map[y-1][x+2]] || com.mans[map[y-1][x+2]].my!=my)) d.push([x+2,y-1]);
                //4点
                if ( y+1<= 9 && x+2<= 8 && !play.map[y][x+1] &&(!com.mans[map[y+1][x+2]] || com.mans[map[y+1][x+2]].my!=my)) d.push([x+2,y+1]);
                //5点
                if ( y+2<= 9 && x+1<= 8 && !play.map[y+1][x] &&(!com.mans[map[y+2][x+1]] || com.mans[map[y+2][x+1]].my!=my)) d.push([x+1,y+2]);
                //7点
                if ( y+2<= 9 && x-1>= 0 && !play.map[y+1][x] &&(!com.mans[map[y+2][x-1]] || com.mans[map[y+2][x-1]].my!=my)) d.push([x-1,y+2]);
                //8点
                if ( y+1<= 9 && x-2>= 0 && !play.map[y][x-1] &&(!com.mans[map[y+1][x-2]] || com.mans[map[y+1][x-2]].my!=my)) d.push([x-2,y+1]);
                //10点
                if ( y-1>= 0 && x-2>= 0 && !play.map[y][x-1] &&(!com.mans[map[y-1][x-2]] || com.mans[map[y-1][x-2]].my!=my)) d.push([x-2,y-1]);
                //11点
                if ( y-2>= 0 && x-1>= 0 && !play.map[y-1][x] &&(!com.mans[map[y-2][x-1]] || com.mans[map[y-2][x-1]].my!=my)) d.push([x-1,y-2]);

        return d;
}

//相
com.bylaw.x = function (x,y,map,my){
        var d=[];
        if (my===1){ //红方
                //4点半
                if ( y+2<= 9 && x+2<= 8 && !play.map[y+1][x+1] && (!com.mans[map[y+2][x+2]] || com.mans[map[y+2][x+2]].my!=my)) d.push([x+2,y+2]);
                //7点半
                if ( y+2<= 9 && x-2>= 0 && !play.map[y+1][x-1] && (!com.mans[map[y+2][x-2]] || com.mans[map[y+2][x-2]].my!=my)) d.push([x-2,y+2]);
                //1点半
                if ( y-2>= 5 && x+2<= 8 && !play.map[y-1][x+1] && (!com.mans[map[y-2][x+2]] || com.mans[map[y-2][x+2]].my!=my)) d.push([x+2,y-2]);
                //10点半
                if ( y-2>= 5 && x-2>= 0 && !play.map[y-1][x-1] && (!com.mans[map[y-2][x-2]] || com.mans[map[y-2][x-2]].my!=my)) d.push([x-2,y-2]);
        }else{
                //4点半
                if ( y+2<= 4 && x+2<= 8 && !play.map[y+1][x+1] && (!com.mans[map[y+2][x+2]] || com.mans[map[y+2][x+2]].my!=my)) d.push([x+2,y+2]);
                //7点半
                if ( y+2<= 4 && x-2>= 0 && !play.map[y+1][x-1] && (!com.mans[map[y+2][x-2]] || com.mans[map[y+2][x-2]].my!=my)) d.push([x-2,y+2]);
                //1点半
                if ( y-2>= 0 && x+2<= 8 && !play.map[y-1][x+1] && (!com.mans[map[y-2][x+2]] || com.mans[map[y-2][x+2]].my!=my)) d.push([x+2,y-2]);
                //10点半
                if ( y-2>= 0 && x-2>= 0 && !play.map[y-1][x-1] && (!com.mans[map[y-2][x-2]] || com.mans[map[y-2][x-2]].my!=my)) d.push([x-2,y-2]);
        }
        return d;
}

//士
com.bylaw.s = function (x,y,map,my){
        var d=[];
        if (my===1){ //红方
                //4点半
                if ( y+1<= 9 && x+1<= 5 && (!com.mans[map[y+1][x+1]] || com.mans[map[y+1][x+1]].my!=my)) d.push([x+1,y+1]);
                //7点半
                if ( y+1<= 9 && x-1>= 3 && (!com.mans[map[y+1][x-1]] || com.mans[map[y+1][x-1]].my!=my)) d.push([x-1,y+1]);
                //1点半
                if ( y-1>= 7 && x+1<= 5 && (!com.mans[map[y-1][x+1]] || com.mans[map[y-1][x+1]].my!=my)) d.push([x+1,y-1]);
                //10点半
                if ( y-1>= 7 && x-1>= 3 && (!com.mans[map[y-1][x-1]] || com.mans[map[y-1][x-1]].my!=my)) d.push([x-1,y-1]);
        }else{
                //4点半
                if ( y+1<= 2 && x+1<= 5 && (!com.mans[map[y+1][x+1]] || com.mans[map[y+1][x+1]].my!=my)) d.push([x+1,y+1]);
                //7点半
                if ( y+1<= 2 && x-1>= 3 && (!com.mans[map[y+1][x-1]] || com.mans[map[y+1][x-1]].my!=my)) d.push([x-1,y+1]);
                //1点半
                if ( y-1>= 0 && x+1<= 5 && (!com.mans[map[y-1][x+1]] || com.mans[map[y-1][x+1]].my!=my)) d.push([x+1,y-1]);
                //10点半
                if ( y-1>= 0 && x-1>= 3 && (!com.mans[map[y-1][x-1]] || com.mans[map[y-1][x-1]].my!=my)) d.push([x-1,y-1]);
        }
        return d;
               
}

//将
com.bylaw.j = function (x,y,map,my){
        var d=[];
        var isNull=(function (y1,y2){
                var y1=com.mans["j0"].y;
                var x1=com.mans["J0"].x;
                var y2=com.mans["J0"].y;
                for (var i=y1-1; i>y2; i--){
                        if (map[i][x1]) return false;
                }
                return true;
        })();
       
        if (my===1){ //红方
                //下
                if ( y+1<= 9  && (!com.mans[map[y+1][x]] || com.mans[map[y+1][x]].my!=my)) d.push([x,y+1]);
                //上
                if ( y-1>= 7 && (!com.mans[map[y-1][x]] || com.mans[map[y-1][x]].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]);
               
        }else{
                //下
                if ( y+1<= 2  && (!com.mans[map[y+1][x]] || com.mans[map[y+1][x]].my!=my)) d.push([x,y+1]);
                //上
                if ( y-1>= 0 && (!com.mans[map[y-1][x]] || com.mans[map[y-1][x]].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]);
        }
        //右
        if ( x+1<= 5  && (!com.mans[map[y][x+1]] || com.mans[map[y][x+1]].my!=my)) d.push([x+1,y]);
        //左
        if ( x-1>= 3 && (!com.mans[map[y][x-1]] || com.mans[map[y][x-1]].my!=my))d.push([x-1,y]);
        return d;
}

//炮
com.bylaw.p = function (x,y,map,my){
        var d=[];
        //左侧检索
        var n=0;
        for (var i=x-1; i>= 0; i--){
                if (map[y][i]) {
                        if (n==0){
                                n++;
                                continue;
                        }else{
                                if (com.mans[map[y][i]].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 (map[y][i]) {
                        if (n==0){
                                n++;
                                continue;
                        }else{
                                if (com.mans[map[y][i]].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 (map[i][x]) {
                        if (n==0){
                                n++;
                                continue;
                        }else{
                                if (com.mans[map[i][x]].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 (map[i][x]) {
                        if (n==0){
                                n++;
                                continue;
                        }else{
                                if (com.mans[map[i][x]].my!=my) d.push([x,i]);
                                break       
                        }
                }else{
                        if(n==0) d.push([x,i])       
                }
        }
        return d;
}

//卒
com.bylaw.z = function (x,y,map,my){
        var d=[];
        if (my===1){ //红方
                //上
                if ( y-1>= 0 && (!com.mans[map[y-1][x]] || com.mans[map[y-1][x]].my!=my)) d.push([x,y-1]);
                //右
                if ( x+1<= 8 && y<=4  && (!com.mans[map[y][x+1]] || com.mans[map[y][x+1]].my!=my)) d.push([x+1,y]);
                //左
                if ( x-1>= 0 && y<=4 && (!com.mans[map[y][x-1]] || com.mans[map[y][x-1]].my!=my))d.push([x-1,y]);
        }else{
                //下
                if ( y+1<= 9  && (!com.mans[map[y+1][x]] || com.mans[map[y+1][x]].my!=my)) d.push([x,y+1]);
                //右
                if ( x+1<= 8 && y>=6  && (!com.mans[map[y][x+1]] || com.mans[map[y][x+1]].my!=my)) d.push([x+1,y]);
                //左
                if ( x-1>= 0 && y>=6 && (!com.mans[map[y][x-1]] || com.mans[map[y][x-1]].my!=my))d.push([x-1,y]);
        }
       
        return d;
}
复制代码
2、根据各个走法,去生成新的棋谱,然后搜索新的走法
3、评估
评估是个很麻烦的事情,一开始我考虑的是给每个棋子价值,比如
士 20  象 20  马 90  车 200  炮 100  兵 40

不过会有一个问题,每个棋子在不同的局面,不同的位置,其价值是不同的,最明显的就是“士”,过河以后明显价值增加了不少,而且越靠近对方老将价值越大,甚至到最后会超越马或者炮,所以我把价值评估做了改进,详情看源码com.value这块。这里的棋子价值,借鉴了一些其他象棋游戏的经验
com.value = {
       
        //车价值
        c:[
                [206, 208, 207, 213, 214, 213, 207, 208, 206],
                [206, 212, 209, 216, 233, 216, 209, 212, 206],
                [206, 208, 207, 214, 216, 214, 207, 208, 206],
                [206, 213, 213, 216, 216, 216, 213, 213, 206],
                [208, 211, 211, 214, 215, 214, 211, 211, 208],
               
                [208, 212, 212, 214, 215, 214, 212, 212, 208],
                [204, 209, 204, 212, 214, 212, 204, 209, 204],
                [198, 208, 204, 212, 212, 212, 204, 208, 198],
                [200, 208, 206, 212, 200, 212, 206, 208, 200],
                [194, 206, 204, 212, 200, 212, 204, 206, 194]
        ],
       
        //马价值
        m:[
                [90, 90, 90, 96, 90, 96, 90, 90, 90],
                [90, 96,103, 97, 94, 97,103, 96, 90],
                [92, 98, 99,103, 99,103, 99, 98, 92],
                [93,108,100,107,100,107,100,108, 93],
                [90,100, 99,103,104,103, 99,100, 90],
               
                [90, 98,101,102,103,102,101, 98, 90],
                [92, 94, 98, 95, 98, 95, 98, 94, 92],
                [93, 92, 94, 95, 92, 95, 94, 92, 93],
                [85, 90, 92, 93, 78, 93, 92, 90, 85],
                [88, 85, 90, 88, 90, 88, 90, 85, 88]
        ],
       
        //相价值
        x:[
                [0, 0,20, 0, 0, 0,20, 0, 0],
                [0, 0, 0, 0, 0, 0, 0, 0, 0],
                [0, 0, 0, 0,23, 0, 0, 0, 0],
                [0, 0, 0, 0, 0, 0, 0, 0, 0],
                [0, 0,20, 0, 0, 0,20, 0, 0],
               
                [0, 0,20, 0, 0, 0,20, 0, 0],
                [0, 0, 0, 0, 0, 0, 0, 0, 0],
                [18,0, 0, 0,23, 0, 0, 0,18],
                [0, 0, 0, 0, 0, 0, 0, 0, 0],
                [0, 0,20, 0, 0, 0,20, 0, 0]
        ],
       
        //士价值
        s:[
                [0, 0, 0,20, 0,20, 0, 0, 0],
                [0, 0, 0, 0,23, 0, 0, 0, 0],
                [0, 0, 0,20, 0,20, 0, 0, 0],
                [0, 0, 0, 0, 0, 0, 0, 0, 0],
                [0, 0, 0, 0, 0, 0, 0, 0, 0],
               
                [0, 0, 0, 0, 0, 0, 0, 0, 0],
                [0, 0, 0, 0, 0, 0, 0, 0, 0],
                [0, 0, 0,20, 0,20, 0, 0, 0],
                [0, 0, 0, 0,23, 0, 0, 0, 0],
                [0, 0, 0,20, 0,20, 0, 0, 0]
        ],
       
        //奖价值
        j:[
                [0, 0, 0, 8888, 8888, 8888, 0, 0, 0],
                [0, 0, 0, 8888, 8888, 8888, 0, 0, 0],
                [0, 0, 0, 8888, 8888, 8888, 0, 0, 0],
                [0, 0, 0, 0, 0, 0, 0, 0, 0],
                [0, 0, 0, 0, 0, 0, 0, 0, 0],
               
                [0, 0, 0, 0, 0, 0, 0, 0, 0],
                [0, 0, 0, 0, 0, 0, 0, 0, 0],
                [0, 0, 0, 8888, 8888, 8888, 0, 0, 0],
                [0, 0, 0, 8888, 8888, 8888, 0, 0, 0],
                [0, 0, 0, 8888, 8888, 8888, 0, 0, 0]
        ],
       
        //炮价值
        p:[
               
                [100, 100,  96, 91,  90, 91,  96, 100, 100],
                [ 98,  98,  96, 92,  89, 92,  96,  98,  98],
                [ 97,  97,  96, 91,  92, 91,  96,  97,  97],
                [ 96,  99,  99, 98, 100, 98,  99,  99,  96],
                [ 96,  96,  96, 96, 100, 96,  96,  96,  96],
               
                [ 95,  96,  99, 96, 100, 96,  99,  96,  95],
                [ 96,  96,  96, 96,  96, 96,  96,  96,  96],
                [ 97,  96, 100, 99, 101, 99, 100,  96,  97],
                [ 96,  97,  98, 98,  98, 98,  98,  97,  96],
                [ 96,  96,  97, 99,  99, 99,  97,  96,  96]
        ],
       
        //卒价值
        z:[
                [ 9,  9,  9, 11, 13, 11,  9,  9,  9],
                [19, 24, 34, 42, 44, 42, 34, 24, 19],
                [19, 24, 32, 37, 37, 37, 32, 24, 19],
                [19, 23, 27, 29, 30, 29, 27, 23, 19],
                [14, 18, 20, 27, 29, 27, 20, 18, 14],
               
                [ 7,  0, 13,  0, 16,  0, 13,  0,  7],
                [ 7,  0,  7,  0, 15,  0,  7,  0,  7],
                [ 0,  0,  0,  0,  0,  0,  0,  0,  0],
                [ 0,  0,  0,  0,  0,  0,  0,  0,  0],
                [ 0,  0,  0,  0,  0,  0,  0,  0,  0]
        ]
}

//黑子为红字价值位置的倒置
com.value.C = com.arr2Clone(com.value.c).reverse();
com.value.M = com.arr2Clone(com.value.m).reverse();
com.value.X = com.value.x;
com.value.S = com.value.s;
com.value.J = com.value.j;
com.value.P = com.arr2Clone(com.value.p).reverse();
com.value.Z = com.arr2Clone(com.value.z).reverse();
复制代码
有了价值,评估就是很简单的事啊,双方健在棋子的价值差,就是这个分支的最后得分。
//评估棋局 取得棋盘双方棋子价值差
AI.evaluate = function (map,my){
        var val=0;
        for (var i=0; i<map.length; i++){
                for (var n=0; n<map[i].length; n++){
                        var key = map[i][n];
                        if (key){
                                val += play.mans[key].value[i][n] * play.mans[key].my;
                        }
                }
        }
        val+=Math.floor( Math.random() * 10);  //让AI走棋增加随机元素
        AI.number++;
        return val*my;
}
复制代码
给我一个指点,我能撬动地球
有了这个算法,我可以骄傲的说只要给我足够强大的计算机,和足够长的计算时间,我就能算出必胜的走棋方法,信不,你信不信反正我是信了。

不过我大概统计了一下,中国象棋平均每步大概有42个走法,如果这盘期走了100个回合的话,那么就是42的200次方个结果,大概需要计算这么多分支,4.4653764701754888195833543328375e+324,大概也就够现在最强大的服务器计算几百年的工作量吧。

看来是不现实的,还是看看现实一点的吧

最大最小搜索

只搜到某个深度,在这个深度来进行评估,决定AI走哪一步棋。
这个时候又有个问题,我走棋以后,对手也不一定选择哪步期来对付我啊,我该怎么判断对手会走哪一步呢,想这个问题之前,看看下面的题目就懂了

 

现在有30个桔子,有大的有小的,有好的有烂的,我们按照好坏级别给他评分了
随机平分为3堆,两个人抢桔子,规则是,A选择其中的一堆,B在A选的那堆里面,选一个给A,如果你是A,你该怎么选呢?
选NO1很有诱惑力,因为里面有最好的桔子16,不过你选了NO1后,你是得不到16的,因为B会拿-6给你,这样的话,对你最有优势的选择应该是NO3,也就是我要选的是每堆里面最次的那个桔子,里面最好的所在的那一堆,比较绕,不知道你听明白没。

走棋也是一样的,对方肯定是会选择对他损失最小的走法来对付你,是的,这就是最大最小算法的原理,想想如何应用到程序中。

好了,AI的基础算法完成了,依靠这套理论就能写出能够走棋的AI了,欢呼吧,虽然性能很低,虽然走得傻傻的。

不知道你能看明白不,确实比较绕,加上本人表达能力有限,有啥意见多提啊,下次改进

下一讲会说说最大最小搜索的优化算法,单凭最大最小搜索效率很低,具体的下期见吧
友情提示:
信息收集于互联网,如果您发现错误或造成侵权,请及时通知本站更正或删除,具体联系方式见页面底部联系我们,谢谢。

其他相似内容:

热门推荐: