zoukankan      html  css  js  c++  java
  • 俄罗斯方块游戏自动机

    《用electron制作俄罗斯方块游戏》 后续文章,智能程序玩俄罗斯方块游戏。

    背景

    前不久用ES6完成了基本的俄罗斯方块游戏,今天已经完成了一个初步的智能算法,可以自动玩俄罗斯方块了,让自己的想法朝实现更近了一步。

    效果图

    第一次运行,消除了1398行,窃喜!

    程序结构

    主要关注智能算法,结构简单化,全部放在了index.js中。

    用定时器驱动游戏

    function autoPlayClick(){
        isAutoPlay = document.getElementById('autoPlay').checked;
        if(isAutoPlay){
            clearInterval(interval)
            interval = setInterval( autoTick, 1 );        //自动算法入口
        }else{
            clearInterval(interval)
            interval = setInterval( tick, TICKVAL );      //自动下落入口
        }
        document.getElementById('speedShow').focus();
    }

    变量定义

    模拟手动操作,一个方块分三步走:旋转、左或右移、下落到底。

    const MOVEOPS = ['moveLeft','moveRight']                //左、右移动定义
    var opList = [], bestEva;                               //待操作队列
    
    class Opration{                                         //操作细节
        constructor(op,num){                    
            this.op = op;                                   //操作方法
            this.num = num;                                 //操作次数
        }
    }
    
    class Evaluation{                                      //局面评估函数结果定义
        constructor(r,x,eva){
            this.r = r;                                    //旋转次数
            this.x = x;                                    //方块水平位置
            this.eva = eva;                                //评估结果
        }
    }

    智能算法调度

    function autoTick(){
        if(opList.length == 0){                            //上个方块已经处理完毕
            getStrategy();                                 //策略算法,生成下一个方块的操作方法策略序列
        }else{
            let op = opList.shift();                       //取操作方法
            for(let i=0; i<op.num; i++){                   //执行既定策略
                tetris[op.op]();
                if(op.op == 'moveDown')                    //是下落操作,取下一块方块
                    generateNext();
            }
        }
    }

    策略算法

    这是算法核心,确定每一块方块的操作方法。

    function getStrategy(){
        let max = 0;                                        //存最优评估值
        tetris.erase();
        let tmp = new Tetris(tetris.shape,tetris.ctx,tetris.x,tetris.y,'rgb(1,1,1,1)','rgb(111,111,111)')                                                //生成用于测试的方块
        for(let i = 0; i < 4; i++){                        //让测试方块与真实方块保持一致,因为我的每一个方块生成时都进行了随机次数的旋转
            for(let j = 0; j < 4; j++){
                tmp.data[i][j] = tetris.data[i][j];  
            }
        }
        for(let r = 0; r < 4; r++){                    //每个方块,旋转四次,分别进行局面评估
            tmp.erase();
            tmp.x = tetris.x;
            tmp.y = tetris.y;
            if(r > 0)
                tmp.rotate();
            while(tmp.moveLeft());                                //从最左测试到右
            while(tmp.moveDown());                                //下落到底
            while(rightOver(tmp)){                                //到右结束这一形态的评估
                let score = evaluate(tmp);                        //局面评估
                if(score > max){                                  //保存最优结果
                    max = score;
                    bestEva = new Evaluation(r,tmp.x,max)
                }
                if(!tmp.moveRight()){                            //右移失败
                    if(!tmp.moveUp()){                           //上移,绕过障碍
                        max = 1000;                              //上移失败,说明填补了空洞,方块就放这
                        bestEva = new Evaluation(r,tmp.x,max)
                        break;
                    }
                }else{
                    while(tmp.moveDown());                      //右移成功后下落到底
                }
            }
            let score = evaluate(tmp);                          //最后一个位置
            if(score > max){
                max = score;
                bestEva = new Evaluation(r,tmp.x,max)
            }
        }
        tmp.erase();
        // console.log(max)
    
        opList.push(new Opration('rotate',bestEva.r));        //旋转操作
        let moveAct = bestEva.x - tetris.x > 0 ? 1 : 0;       //水平位置差转化成左或右移操作
        let actNum = Math.abs(bestEva.x - tetris.x)
        opList.push(new Opration(MOVEOPS[moveAct],actNum));   //左或右移操作
        opList.push(new Opration('moveDown',1));              //下落操作
    
    }

    评估函数

    现在只做了几个基本参数评估,有待优化。更深入的做法是加入机器学习算法,进行自主反馈学习。

    function evaluate(t){
        let ct = t.y;                                                            //调试越大越好
        for(let i = 0; i < 4; i++){                                              //查看每个小方块的四个邻居的情况
            for(let j = 0; j < 4; j++){
                if(t.data[i][j]){
                    if(t.canSee(t.x +i, t.y + j + 1))                            //下方是空洞
                        ct -= 5;
                    for(let k=0; k<4; k++){
                        switch(k){
                            case 0: ct += t.canSee(t.x + i + 1, t.y + j) ? 0 : 1;   //右
                            break;
                            case 1: ct += t.canSee(t.x + i - 1, t.y + j) ? 0 : 1;   //左
                            break;
                            case 2: ct += t.canSee(t.x + i, t.y + j + 1) ? 0 : 1;   //下
                            break;
                            case 3: ct += t.canSee(t.x + i, t.y + j - 1) ? 0 : 1;   //上
                            break;
                        }
                    }
                }
            }
        }
        return ct;
    }

    源代码:

    git clone https://git.oschina.net/zhoutk/Tetris.git
    或者:
    git clone https://github.com/zhoutk/Tetris

    小结

    开启了我的智能算法学习之路,这还只是一个最简单的自动程序,都谈不上任何智能,但对我来说是一个新方向的开始,加油!

  • 相关阅读:
    BZOJ1001 BJOI2006狼抓兔子(最小割+最短路)
    BZOJ4569 SCOI2016萌萌哒(倍增+并查集)
    Luogu4782 【模板】2-SAT 问题(2-SAT)
    BZOJ3626 LNOI2014LCA(树链剖分+主席树)
    BZOJ4012 HNOI2015开店(树链剖分+主席树)
    Luogu2264 树上游戏(点分治)
    BZOJ3998 TJOI2015弦论(后缀数组+二分答案)
    BZOJ1045 HAOI2008糖果传递(贪心)
    BZOJ1124 POI2008枪战Maf(环套树+贪心)
    洛谷 P4568 [JLOI2011]飞行路线 解题报告
  • 原文地址:https://www.cnblogs.com/zhoutk/p/5540786.html
Copyright © 2011-2022 走看看