javascript 实现五子棋人机PK-v2

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <style type="text/css">
        #box1 table{
            border: 0px;
            border-collapse: collapse;
            cursor: pointer;
        }

        #box1 th {
            border: 0px;
            border-collapse: collapse;
            cursor: pointer;
        }

         #box1 td{
            border: 1px solid #000;
            border-collapse: collapse;
            cursor: pointer;
         }


        #box2 table{
            border-collapse: collapse;
            border: 0px;
        }

        #box2 th {
            border-collapse: collapse;
            border: 0px;
        }

        #box2 td{
            border-collapse: collapse;
            border: 1px solid transparent;
            border-radius:20px;
        }

       .color_human{
         background-color: white;
       }

       .color_machine{
         background-color: black;
       }

       .color_machine_win{
            background-color: red;
       }

        .color_human_win{
            background-color: green;
       }

        #box1{
           position: absolute;
           left: 100px;
           top: 100px;
           border: 0px;
           background-color: gray;
       }

        #box2{
           position: absolute;
           left: 110px;
           top: 110px;
           border: 0px;
       }
    </style>
</head>
<body>
    <div id="box1"></div>
    <div id="box2"></div>
</body>
<script type="text/javascript">
    var map_code_td = new Map();
    var map_code_color = new Map();
    var map_code_score = new Map();
    var win = false;
    var color_human = "color_human";
    var color_machine = "color_machine"
    var codes_win = [];
    var color_blank = "xxx";
    var row_count = 20;
    var col_count = 20;

    // 辅助帮助显示横竖纹的底色棋盘,没有任何事件和业务逻辑
    function initBox1( ) {
        var table = document.createElement("table");
        table.rules  = 'all' ;
        for (var rowNum = 1; rowNum <= row_count; rowNum++) {
            var tr = document.createElement("tr");
            for (var colNum = 1; colNum <= col_count; colNum++) {
                var td = document.createElement("td");
                td.width = 20;
                td.height = 20;
                tr.appendChild(td);
            }
            table.appendChild(tr);
        }
        document.getElementById( "box1" ).appendChild(table);
    }

    function initBox2( ) {
        var table = document.createElement("table");
        table.rules  = 'all' ;
        for (var rowNum = 1; rowNum <= row_count; rowNum++) {
            var tr = document.createElement("tr");
            for (var colNum = 1; colNum <= col_count; colNum++) {
                var td = document.createElement("td");
                td.width = 20;
                td.height = 20;
                tr.appendChild(td);

                var code = rowNum + "_" + colNum;
                map_code_td.set( code,td );
                map_code_color.set( code,color_blank );
            }
            table.appendChild(tr);
        }
        document.getElementById( "box2" ).appendChild(table);
        table.addEventListener( "click", clickChess );
    }

    initBox1(  );
    initBox2(  );

    // 获取到点击的 td,如果该人类落子,则将td的颜色改为 人类棋子颜色,否则该机器落子,将颜色改为机器棋子颜色
    var shouldHumanPoint = true;
    function clickChess(){
        if( win ){
            return;
        }
        if( !shouldHumanPoint ){
            alert("稍等,机器落子后您再落子!");
            return;
        }
        var td = event.srcElement;
        var rowNum = td.parentElement.rowIndex + 1;
        var colNum = td.cellIndex + 1;
        if( isAlreadyFillPoint( rowNum,colNum ) ){
            alert( "该位置已落子" );
            return;
        }

        // 执行人类落子操作
        td.className = color_human;
        var code = rowNum + "_" + colNum;
        map_code_color.set( code,color_human );
        shouldHumanPoint = false;

        // 人类落子后检测是否胜利
        winCheck();
        if( win ){
            highlightFiveWinCodes( true );
            alert("人类胜利");
            return;
        }

        window.setTimeout(function(){
            // 机器开始落子
            machinePoint();

            // 机器落子后检测是否胜利
            winCheck();
            if( win ){
                highlightFiveWinCodes( false );
                alert("机器胜利");
            }
        },1000);
    }

    // 高亮5个连成线的棋子
    function highlightFiveWinCodes( humanWin ){
        for(var i=0;i<5;i++){
            var td = map_code_td.get(  codes_win[i] );
            if( humanWin ){
                td.className = "color_human_win";
            }else{
                td.className = "color_machine_win";
            }
        }
    }

    // 机器落子操作
    function machinePoint(){
        // 首先检查人类是否存在机器不拦则人类必胜的情况,比如存在人类的 "空白白白空空"两端的 空的数量满足一端>=1 ,一端>1,则机器必须落在 "白白白"的两端
        // 空白白白空空、空空白白白空
        // 人类必胜检测
        // 人类必胜的六连子,检测到有,则机器落在六连子的最后一个子上
        var sixCodes = humanMustWinCheck();
        if( sixCodes ){
            console.log("检测到存在人类的必胜六连子,试图拦截:");
            console.log(sixCodes);
            var code = sixCodes[5];
            var td = map_code_td.get( code );
            td.className = color_machine;
            map_code_color.set( code,color_machine );
            shouldHumanPoint = true;
            return;
        }

        // 机器开始落子,需要找到一个得分最高的格子开始落子
        // 从下面 row_count * col_count 格子中找到得分最高的一个格子
        var maxScore = 0;
        var rowNum_maxScore = 0;
        var colNum_maxScore = 0;
        for( var rowNum = 1;rowNum <= row_count;rowNum++ ){
            for( var colNum = 1;colNum <= col_count;colNum++ ){
                if( isAlreadyFillPoint( rowNum,colNum ) ){
                    // 该位置已经落子了,不考虑
                    continue;
                }
                var score = calculateScore( rowNum, colNum );
                if( score > maxScore ){
                    maxScore = score;
                    rowNum_maxScore = rowNum;
                    colNum_maxScore = colNum;
                }
            }
        }

        console.log( "机器应该落在" + rowNum_maxScore + "行" + colNum_maxScore + "列,得分:" + maxScore );
        var code = rowNum_maxScore + "_" + colNum_maxScore;
        var td = map_code_td.get( code );
        td.className = color_machine;
        map_code_color.set( code,color_machine );
        shouldHumanPoint = true;
    }

    // 检测指定位置是否已经落子了
    function isAlreadyFillPoint( rowNum,colNum ){
        var code = rowNum + "_" + colNum;
        var color = map_code_color.get( code );
        if( color == color_human || color == color_machine ){
            return true;
        }
        return false;
    }

    // 得分表,该程序的精髓,其实本程序未使用任何算法,只是使用了这个得分表,写这个得分表的人比较厉害,程序厉不厉害也是取决于得分表设计的精不精妙
    function calculateScoreForForHumanMachineColorCount( count_humanColor,count_machineColor){
        if( count_humanColor > 0 && count_machineColor > 0 ){
            return 0;
        }else if( count_humanColor == 0 && count_machineColor == 0 ){
            return 7;
        }else if( count_humanColor == 0 && count_machineColor == 1 ){
            return 35;
        }else if( count_humanColor == 0 && count_machineColor == 2 ){
            return 800;
        }else if( count_humanColor == 0 && count_machineColor == 3 ){
            return 15000;
        }else if( count_humanColor == 0 && count_machineColor == 4 ){
            return 800000;
        }else if( count_humanColor == 1 && count_machineColor == 0 ){
            return 15;
        }else if( count_humanColor == 2 && count_machineColor == 0 ){
            return 400;
        }else if( count_humanColor == 3 && count_machineColor == 0 ){
            return 1800;
        }else if( count_humanColor == 4 && count_machineColor == 0 ){
            return 100000;
        }else{
            return 0;
        }
    }

    // 为该五连子计算得分
    function calculateScoreForFiveCodes( fiveCodes ){
        // 统计人类棋子和机器棋子的个数
        var count_humanColor = 0;
        var count_machineColor = 0;
        for( var i=0;i<5;i++ ){
            var code = fiveCodes[i];
            var color = map_code_color.get( code );
            if( !color ){
                // 可能由于传递的 fiveCodes 不是有效的五连子导致的,比如 "16_1"、"-1_2"等,因为前面未进行最大最小合法角标的限制
                return 0;
            }
            if( color == color_human ){
                count_humanColor++;
            }else if( color == color_machine ){
                count_machineColor++;
            }
        }
        return calculateScoreForForHumanMachineColorCount( count_humanColor, count_machineColor );
    }

    function calculateScore(rowNum, colNum){
        var totalScore = 0;
        // 该格子所在行( 左-->右:行不变,列++ )
        // 统计该格子作为第1、2、3、4、5个子时候的五连子的得分之和
        for( var i=0; i<5; i++){
            var fiveCodes = [];
            fiveCodes.push( rowNum + "_" + ( colNum + 0 - i ) );
            fiveCodes.push( rowNum + "_" + ( colNum + 1 - i ) );
            fiveCodes.push( rowNum + "_" + ( colNum + 2 - i ) );
            fiveCodes.push( rowNum + "_" + ( colNum + 3 - i ) );
            fiveCodes.push( rowNum + "_" + ( colNum + 4 - i ) );
            totalScore += calculateScoreForFiveCodes( fiveCodes );
        }

        // 该格子所在列( 上->下:行++,列不变 )
        // 统计该格子作为第1、2、3、4、5个子时候的五连子的得分之和
        for( var i=0; i<5; i++){
            var fiveCodes = [];
            fiveCodes.push( ( rowNum + 0 - i ) + "_" + colNum );
            fiveCodes.push( ( rowNum + 1 - i ) + "_" + colNum );
            fiveCodes.push( ( rowNum + 2 - i ) + "_" + colNum );
            fiveCodes.push( ( rowNum + 3 - i ) + "_" + colNum );
            fiveCodes.push( ( rowNum + 4 - i ) + "_" + colNum );
            totalScore += calculateScoreForFiveCodes( fiveCodes );
        }

        // 该格子所在正斜线( 左下-->右上:行--,列++ )
        // 统计该格子作为第1、2、3、4、5个子时候的五连子的得分之和
        for( var i=0; i<5; i++){
            var fiveCodes = [];
            fiveCodes.push( ( rowNum + i - 0 ) + "_" + ( colNum + 0 - i ) );
            fiveCodes.push( ( rowNum + i - 1 ) + "_" + ( colNum + 1 - i ) );
            fiveCodes.push( ( rowNum + i - 2 ) + "_" + ( colNum + 2 - i ) );
            fiveCodes.push( ( rowNum + i - 3 ) + "_" + ( colNum + 3 - i ) );
            fiveCodes.push( ( rowNum + i - 4 ) + "_" + ( colNum + 4 - i ) );
            totalScore += calculateScoreForFiveCodes( fiveCodes );
        }

        // 该格子所在反斜线( 左上-->右下:行++,列++ )
        // 统计该格子作为第1、2、3、4、5个子时候的五连子的得分之和
        for( var i=0; i<5; i++){
            var fiveCodes = [];
            fiveCodes.push( ( rowNum + 0 - i )  + "_" + ( colNum + 0 - i ) );
            fiveCodes.push( ( rowNum + 1 - i ) + "_" +  ( colNum + 1 - i ) );
            fiveCodes.push( ( rowNum + 2 - i ) + "_" +  ( colNum + 2 - i ) );
            fiveCodes.push( ( rowNum + 3 - i ) + "_" +  ( colNum + 3 - i ) );
            fiveCodes.push( ( rowNum + 4 - i ) + "_" +  ( colNum + 4 - i ) );
            totalScore += calculateScoreForFiveCodes( fiveCodes );
        }
        return totalScore;
    }

    function winCheck(){
        if( win ){
            return;
        }
        // 检查全部的五连子,是否存在五个全是人类棋子或者五个全是机器棋子
        // 检查全部列上的五连子
        winCheck_col()
        if( win ){
            return;
        }

        // 检查全部行上的五连子
        winCheck_row();
        if( win ){
            return;
        }

        // 检查全部正斜线上的五连子
        winCheck_forwardSlash();
        if( win ){
            return;
        }

        // 检查全部反斜线上的五连子
        winCheck_backSlash();
        if( win ){
            return;
        }
    }

    function winCheck_forwardSlash(){
        // 列--,行++
        for( rowNum=1;rowNum<=row_count;rowNum++ ){
            for( colNum=1;colNum<=col_count;colNum++ ){
                var codes = [];
                codes.push( rowNum + "_" + colNum );
                codes.push( ( rowNum + 1 ) + "_" + ( colNum - 1 ) );
                codes.push( ( rowNum + 2 ) + "_" + ( colNum - 2 ) );
                codes.push( ( rowNum + 3 ) + "_" + ( colNum - 3 ) );
                codes.push( ( rowNum + 4 ) + "_" + ( colNum - 4 ) );
                if( isAllTargetColor( codes,color_human ) || isAllTargetColor( codes,color_machine ) ){
                    win = true;
                }
                if( win ){
                    for(var i=0;i<5;i++){
                        codes_win.push( codes[i] );
                    }
                    return;
                }
            }
        }
    }

    function winCheck_backSlash(){
         // 列++,行++
        for( rowNum=1;rowNum<=row_count;rowNum++ ){
            for( colNum=1;colNum<=col_count;colNum++ ){
                var fiveCodes = [];
                fiveCodes.push( rowNum + "_" + colNum );
                fiveCodes.push( ( rowNum + 1 ) + "_" + ( colNum + 1 ) );
                fiveCodes.push( ( rowNum + 2 ) + "_" + ( colNum + 2 ) );
                fiveCodes.push( ( rowNum + 3 ) + "_" + ( colNum + 3 ) );
                fiveCodes.push( ( rowNum + 4 ) + "_" + ( colNum + 4 ) );
                if( isAllTargetColor( fiveCodes,color_human ) || isAllTargetColor( fiveCodes,color_machine ) ){
                    // 都是人类棋子 或者 机器棋子,表示已经分出胜负了
                    win = true;
                }
                if( win ){
                    for( var i=0;i<5;i++ ){
                        codes_win.push( fiveCodes[i] );
                    }
                    return;
                }
            }
        }
    }

    function winCheck_col(){
        // 检查全部列是否存在五连子
        // 列不变,行++
        for( var colNum=1;colNum<=col_count;colNum++ ){
            for( var rowNum=1;rowNum<=row_count;rowNum++ ){
                var fiveCodes = [];
                fiveCodes.push( rowNum + "_" + colNum );
                fiveCodes.push( ( rowNum + 1 ) + "_" + colNum );
                fiveCodes.push( ( rowNum + 2 ) + "_" + colNum );
                fiveCodes.push( ( rowNum + 3 ) + "_" + colNum );
                fiveCodes.push( ( rowNum + 4 ) + "_" + colNum );
                if( isAllTargetColor( fiveCodes,color_human ) || isAllTargetColor( fiveCodes,color_machine ) ){
                    // 已经分出胜负了
                    win = true;
                }
                if( win ){
                    for( var i=0;i<5;i++ ){
                        codes_win.push( fiveCodes[i] );
                    }
                    return;
                }
            }
        }
    }

    function winCheck_row(){
        // 行不变,列++
        for( var rowNum=1;rowNum<=row_count;rowNum++ ){
            for( var colNum=1;colNum<=col_count;colNum++ ){
                var codes = [];
                codes.push( rowNum + "_" + colNum );
                codes.push( rowNum + "_" + ( colNum + 1 ) );
                codes.push( rowNum + "_" + ( colNum + 2 ) );
                codes.push( rowNum + "_" + ( colNum + 3 ) );
                codes.push( rowNum + "_" + ( colNum + 4 ) );
                if( isAllTargetColor( codes,color_human ) || isAllTargetColor( codes,color_machine ) ){
                    // 已经分出胜负了
                    win = true;
                }
                if( win ){
                    for( var i=0;i<5;i++ ){
                        codes_win.push( codes[i] );
                    }
                    return;
                }
            }
        }
    }

    // 检查五连子是否都是指定的颜色
    function  isAllTargetColor( fiveCodes,targetColor ){
        for( var i = 0;i<5;i++ ){
            var code = fiveCodes[i];
            var color = map_code_color.get( code );
            if( !color || color != targetColor ){
                return false;
            }
        }
        return true;
    }

     // 人类必胜检测
    // 检查人类是否存在机器不拦则人类必胜的情况,比如存在人类的 "空白白白空空"两端的 空的数量满足一端>=1 ,一端>1,则机器必须落在 "白白白"的两端
    function humanMustWinCheck(){
        // 全部行的 人类必胜检测
        var sixCodes = humanMustWinCheck_allRow();
        if( sixCodes ){
            return sixCodes;
        }

        // 全部列的 人类必胜检测
        sixCodes = humanMustWinCheck_allCol();
        if( sixCodes ){
            return sixCodes;
        }

        // 全部正斜线的 人类必胜检测
        sixCodes = humanMustWinCheck_allForwardSlash();
        if( sixCodes ){
            return sixCodes;
        }

        // 全部反斜线的 人类必胜检测
        sixCodes = humanMustWinCheck_allBackSlash();
        if( sixCodes ){
            return sixCodes;
        }
    }

     // 全部行的 人类必胜检测
    function humanMustWinCheck_allRow(){
        for( var rowNum=1;rowNum<=row_count;rowNum++ ){
            // 对当前行进行 人类必胜检测
            var sixCodes = humanMustWinCheck_row( rowNum );
            if( sixCodes ){
                return sixCodes;
            }
        }
        return null;
    }

     // 对当前行进行人类必胜检测
    function humanMustWinCheck_row( rowNum ){
        // 遍历当前行的全部六连子,检测是否存在 "空白白白空空"、"空空白白白空"
        for( var colNum=1;colNum<=col_count;colNum++ ){
            var codes = [];
            codes.push( rowNum + "_" + colNum );
            codes.push( rowNum + "_" + ( colNum + 1 ) );
            codes.push( rowNum + "_" + ( colNum + 2 ) );
            codes.push( rowNum + "_" + ( colNum + 3 ) );
            codes.push( rowNum + "_" + ( colNum + 4 ) );
            codes.push( rowNum + "_" + ( colNum + 5 ) );
            if( isHumanMushWinSixCodes( codes ) ){
                return codes;
            }
        }
        return null;
    }

     // 检查是否是人类必胜六连子
    function isHumanMushWinSixCodes(  sixCodes ){
        var color1 = map_code_color.get( sixCodes[0] );
        var color2 = map_code_color.get( sixCodes[1] );
        var color3 = map_code_color.get( sixCodes[2] );
        var color4 = map_code_color.get( sixCodes[3] );
        var color5 = map_code_color.get( sixCodes[4] );
        var color6 = map_code_color.get( sixCodes[5] );
        if( !( color1 && color2 && color3 && color4 && color5 && color6 ) ){
            return false;
        }
        if( ( color1 == color_blank &&
                color2 == color_blank &&
                color3 == color_human &&
                color4 == color_human &&
                color5 == color_human &&
                color6 == color_blank ) ||
            ( color1 == color_blank &&
                color2 == color_human &&
                color3 == color_human &&
                color4 == color_human &&
                color5 == color_blank &&
                color6 == color_blank ) ){
            return true;
        }else{
            return false;
        }
    }

     // 当前列的人类必胜六连子检测
    function humanMustWinCheck_allCol(colNum){
        for( var rowNum=1;rowNum<=row_count;rowNum++ ){
            var codes = [];
            codes.push( rowNum + "_" + colNum );
            codes.push( ( rowNum + 1 ) + "_" + colNum );
            codes.push( ( rowNum + 2 ) + "_" + colNum );
            codes.push( ( rowNum + 3 ) + "_" + colNum );
            codes.push( ( rowNum + 4 ) + "_" + colNum );
            codes.push( ( rowNum + 5 ) + "_" + colNum );
            if( isHumanMushWinSixCodes( codes ) ){
                return codes;
            }
        }
        return null;
    }

     // 全部正斜线的 人类必胜检测
    function humanMustWinCheck_allForwardSlash(){
       for(var rowNum=1;rowNum<=row_count;rowNum++){
            // 当前正斜线的 人类必胜检测
            var sixCodes = humanMustWinCheck_forwardSlash(rowNum,1);
            if( sixCodes ){
                return sixCodes;
            }
       }
       for(var colNum=1;colNum<=col_count;colNum++){
            // 当前正斜线的 人类必胜检测
            var sixCodes = humanMustWinCheck_forwardSlash(row_count,colNum);
            if( sixCodes ){
                return sixCodes;
            }
       }
       return null;
    }

     //  当前正斜线的 人类必胜检测
    function humanMustWinCheck_forwardSlash( fromRowNum,fromColNum ){
        var large = row_count;
        if( col_count > large ){
            large = col_count;
        }
        for(var i=0;i<large;i++){
            var codes = [];
            codes.push( ( fromRowNum - i ) + "_" + ( fromColNum + i ) );
            codes.push( ( fromRowNum - i - 1 ) + "_" + ( fromColNum + i + 1 ) );
            codes.push( ( fromRowNum - i - 2 ) + "_" + ( fromColNum + i + 2 ) );
            codes.push( ( fromRowNum - i - 3 ) + "_" + ( fromColNum + i + 3 ) );
            codes.push( ( fromRowNum - i - 4 ) + "_" + ( fromColNum + i + 4 ) );
            codes.push( ( fromRowNum - i - 5 ) + "_" + ( fromColNum + i + 5 ) );
            if( isHumanMushWinSixCodes( codes ) ){
                return codes;
            }
        }
        return null;
    }

      // 全部反斜线的 人类必胜检测
    function humanMustWinCheck_allBackSlash(){
        for(var colNum=1;colNum<=col_count;colNum++){
            // 当前反斜线的 人类必胜检测
            var sixCodes = humanMustWinCheck_backSlash( 1,colNum );
            if( sixCodes ){
                return sixCodes;
            }
        }
        for(var rowNum=1;rowNum<=row_count;rowNum++){
            // 当前反斜线的 人类必胜检测
            var sixCodes = humanMustWinCheck_backSlash( rowNum,1 );
            if( sixCodes ){
                return sixCodes;
            }
        }
        return null;
    }

    // 行、列、正斜线、反斜线上的几个子的行号、列号增长的趋势规则(即++、--)可以添加到一个数组中,每次从数组中取
    // 当前反斜线的 人类必胜检测
    function humanMustWinCheck_backSlash(fromRowNum,fromColNum){
        var large = row_count;
        if( col_count > large ){
            large = col_count;
        }
        for( var i=0;i<large;i++ ){
            var codes = [];
            codes.push( ( fromRowNum + i ) + "_" + ( fromColNum + i ) );
            codes.push( ( fromRowNum + i + 1 ) + "_" + ( fromColNum + i + 1 ) );
            codes.push( ( fromRowNum + i + 2 ) + "_" + ( fromColNum + i + 2 ) );
            codes.push( ( fromRowNum + i + 3 ) + "_" + ( fromColNum + i + 3 ) );
            codes.push( ( fromRowNum + i + 4 ) + "_" + ( fromColNum + i + 4 ) );
            codes.push( ( fromRowNum + i + 5 ) + "_" + ( fromColNum + i + 5 ) );
            if( isHumanMushWinSixCodes( codes ) ){
                return codes;
            }
        }
        return null;
    }
</script>
</html>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值