美文网首页cocos2d-Lua
cocos2d-x3.14中国象棋AI(五)移动棋子1

cocos2d-x3.14中国象棋AI(五)移动棋子1

作者: lable | 来源:发表于2017-06-12 21:11 被阅读16次

    我们在选中棋子之后理所当然就是要移动棋子,要移动棋子我们不知需要知道选中的棋子点信息,还需要知道选中棋子后玩家下一次点击屏幕的触摸点信息。知道两点的信息之后我们就可以开始写棋子的移动规则了,这里将函数拆分得细一点,避免一个函数一大串代码,这样不容易管理还容易出错。

    首先我们的移动操作应该是在触摸事件函数中实现,而且是在选中棋子后才可以移动,这样我们就需要在触摸事件的回调函数加一个判断,区分开选中和移动两个功能。

    void LayerGameMain::touchEndedCallBack(Touch *pTouch, Event *pEvent)
    {
        if (_selectedId == -1)//没选中棋子才能选棋
        {
            selectStone(pTouch);
        }
        else//选中棋子后执行移动棋子
        {
            moveStone(pTouch);
        }
    }
    

    接着我们就需要开始写移动棋子函数,移动棋子需要对不同棋子制定不同规则,我们给每种棋子写一个规则判断函数,然后通过switch来筛选需要使用哪种规则,最后再调用一个移动函数实现棋子移动效果。这里先写出车、炮的移动规则(他们之间存在相似点)。
    获取棋子与目标点间的棋子数:

    int LayerGameMain::getStoneCount(int row1, int col1, int row2, int col2)
    {
        int ret = 0;//记录棋子的个数
        if (row1 != row2 && col1 != col2) return -1;//行列都不同,没有直线
        if (row1 == row2 && col1 == col2) return -1;//行列都相同,两点相同,不需要移动
    
        if (row1 == row2)//列相同
        {
            int mincol = col1 < col2 ? col1 : col2;//获取两点间较小的行
            int maxcol = col1 > col2 ? col1 : col2;//获取两点间较大的行
            for (int col = mincol+1; col < maxcol; col++)//遍历两点间所有点
            {
                if (getStoneIdFromRowCol(row1, col) > 0)//如果存在棋子,ret棋子计数器+1
                {
                    ++ret;
                }
            }
        }
        else if(col1 == col2)
        {
            int minrow = row1 < row2 ? row1 : row2;//获取两点间较小的列
            int maxrow = row1 > row2 ? row1 : row2;//获取两点间较大的列
            for (int row = minrow + 1; row < maxrow; row++)//遍历两点间所有点
            {
                if (getStoneIdFromRowCol(row, col1) > 0)//如果存在棋子,ret棋子计数器+1
                {
                    ++ret;
                }
            }
        }
    
        return ret;
    }
    

    移动规则筛选函数:

    
    bool LayerGameMain::canMove(int moveid, int row, int col, int killid)
    {
        Stone *stone = getStoneFromArrById(moveid);
        switch (stone->_type)
        {
            case Stone::CHE:
                return canMoveChe(moveid, row, col);
                break;
            case Stone::PAO:
                return canMovePao(moveid, row, col, killid);
                break;
                    /*余下规则都在这*/
            default:
                break;
        }
        return false;
    }
    

    最后给车和炮写移动规则:

    bool LayerGameMain::canMoveChe(int moveid, int row, int col)
    {
        //车只有在两点间不存在棋子时才能走
        Stone *che = getStoneFromArrById(moveid);
        if (getStoneCount(che->_row, che->_col, row, col)>0)
        {
            false;
        }
        return true;
    }
    bool LayerGameMain::canMovePao(int moveid, int row, int col, int killid)
    {
        //炮可以行走只有两种情况:1.两点间没有棋子。2.两点间存在一个棋子,并且目的点也存在棋子。
        Stone *pao = getStoneFromArrById(moveid);
        if (killid == -1)
        {
            return getStoneCount(pao->_row, pao->_col, row, col) == 0;
        }
    
        return getStoneCount(pao->_row, pao->_col, row, col) == 1;
    }
    

    其他棋子的规则就不多写了,看看代码很容易看懂,这里直接贴出所有规则实现后的LayerGameMain代码。
    LayerGameMain.h

    #ifndef __LAYERGAMEMAIN_H__
    #define __LAYERGAMEMAIN_H__
    
    #include "cocos2d.h"
    #include "Plate.h"
    #include "Stone.h"
    
    /*
    cocos2d-x3.14.1版本将大部分类的前缀CC去掉了,不过有部分还是兼容,如果用旧的命名创建对应的类会有警告提示/错误提示
    */
    
    USING_NS_CC;
    
    class LayerGameMain : public Layer
    {
    public:
        static Scene* createScene();
        virtual bool init();
        CREATE_FUNC(LayerGameMain);
    
        //添加棋盘和棋子
        void addPlate();//用于在LayerGameMain上添加棋盘层/精灵
        void addStones();//用于在LayerGameMain上添加棋子层/精灵
    
        //触摸事件回调函数
        bool touchBeganCallBack(Touch *pTouch, Event *pEvent);
        void touchEndedCallBack(Touch *pTouch, Event *pEvent);
    
        //选中棋子相关
        Sprite *selectBox;//选中款精灵
        static int _selectedId;//标记当前选中的棋子id,没有点中为-1
        bool Screen2Plate(Point &point, int &row, int &col);//循环棋盘上所有点,通过Plate2Screen获取到棋子中心点,用引用输出中心点
        Point Plate2Screen(int row, int col);//获取棋子中心点返回到世界坐标给Screen2Plate
        void selectStone(Touch *pTouch);//选中棋子,将选中框精灵移动到选中的棋子上
    
        //获取棋子相关
        __Array *arrStone;//用于存放棋子
        Stone* getStoneFromArrById(int id);//通过棋子id从arrStone获取对应棋子
        int getStoneIdFromRowCol(int row, int col);//通过行列获取棋子id
    
        //棋子移动相关
        void moveStone(Touch *pTouch);
        int getStoneCount(int row1, int col1, int row2, int col2);
        bool canMove(int moveid, int row, int col, int killid);//筛选移动规则
            //moveid为当前想要移动棋子的id,killid移动的目的点(-1为没有棋子),row和col为目的点的行列
            //炮、兵、将、车移动规则都有点相似
        bool canMoveChe(int moveid, int row, int col);//车移动规则
        bool canMovePao(int moveid, int row, int col, int killid);//炮移动规则
        bool canMoveBing(int moveid, int row, int col);//兵移动规则
        bool canMoveJiang(int moveid, int row, int col, int killid);//将移动规则
        bool canMoveMa(int moveid, int row, int col);//马移动规则
        bool canMoveXiang(int moveid, int row, int col);//象移动规则
        bool canMoveShi(int moveid, int row, int col);//士移动规则
    };
    
    #endif // !__LAYERGAMEMAIN_H__
    

    LayerGameMain.cpp

    #include "LayerGameMain.h"
    
    int  LayerGameMain::_selectedId = -1;
    
    Scene* LayerGameMain::createScene()
    {
        auto ret = Scene::create();
        auto layer = LayerGameMain::create();
        ret->addChild(layer);
    
        return ret;
    }
    
    bool LayerGameMain::init()
    {
        if (!Layer::init())
        {
            return false;
        }
        arrStone = __Array::create();
        arrStone->retain();//计数器+1,否则会被回收
        selectBox = Sprite::create("selected.png");
        selectBox->setVisible(false);
        selectBox->setZOrder(1000);//设置显示优先级
        this->addChild(selectBox);
    
        this->addPlate();
        this->addStones();
    
        //cocos2d-x3.14.1触摸事件,创建监听
        EventDispatcher* eventDispatcher = Director::getInstance()->getEventDispatcher();
        auto listener = EventListenerTouchOneByOne::create();
        listener->setEnabled(true);
        listener->setSwallowTouches(true);
        listener->onTouchBegan = CC_CALLBACK_2(LayerGameMain::touchBeganCallBack, this);
        listener->onTouchEnded = CC_CALLBACK_2(LayerGameMain::touchEndedCallBack, this);
        eventDispatcher->addEventListenerWithSceneGraphPriority(listener, this);
    
        return true;
    }
    
    void LayerGameMain::addPlate()
    {
        Plate *plate = Plate::create();
        
        this->addChild(plate);
    }
    
    void LayerGameMain::addStones()
    {
        int i = 0;
        Stone *stone;
        for (i = 0; i < 32; i++)
        {
            stone = Stone::create(i);
            arrStone->addObject(stone);//将棋子添加进数组
            this->addChild(stone);
        }
    }
    
    Stone* LayerGameMain::getStoneFromArrById(int stoneId)
    {
        Stone *ret;
        ret = nullptr;
        if (stoneId > 0)
        {
            ret = (Stone *)arrStone->getObjectAtIndex(stoneId);
        }
        
        return ret;
    }
    
    bool LayerGameMain::touchBeganCallBack(Touch *pTouch, Event *pEvent)
    {
    
        return true;
    }
    void LayerGameMain::touchEndedCallBack(Touch *pTouch, Event *pEvent)
    {
        if (_selectedId == -1)
        {
            selectStone(pTouch);
        }
        else
        {
            moveStone(pTouch);
        }
    }
    
    bool LayerGameMain::Screen2Plate(Point &point, int &row, int &col)
    {
        int distance = Stone::_d/2 * Stone::_d/2;//以半径作为距离
        for (row = 0; row <= 9; row++)
        {
            for (col = 0; col <= 8; col++)
            {
                Point ptCenter = Plate2Screen(row, col);
                if (distance > ptCenter.getDistanceSq(point))//获取点距的平方并比较,如果小于半径平方则为能选中中心点上的棋子
                {
                    return true;
                }
            }
        }
    
        return false;
    }
    Point LayerGameMain::Plate2Screen(int row, int col)//该函数与Stone的getPositionFromPlate类似
    {
        Point ret = Point(Stone::_offx + col*Stone::_d, Stone::_offy + row*Stone::_d);
        
        return ret;
    }
    int LayerGameMain::getStoneIdFromRowCol(int row, int col)
    {
        int ret = -1;
        Ref *obj;
        CCARRAY_FOREACH(arrStone,obj)
        {
            Stone *stone = (Stone *)obj;
            if (stone->_row == row && stone->_col == col)
            {
                ret = stone->_id;
                return ret;
            }
        }
        return ret;
    }
    
    void LayerGameMain::selectStone(Touch *pTouch)
    {
        Point point = pTouch->getLocation();
        int row, col;
        if (!Screen2Plate(point, row, col))//点中范围不在棋盘上直接返回
        {
            selectBox->setVisible(false);
            return;
        }
        int clickedId = getStoneIdFromRowCol(row, col);
        if (clickedId < 0)//没选中棋子直接返回
        {
            selectBox->setVisible(false);
            return;
        }
        Stone *clickedStone = getStoneFromArrById(clickedId);
    
        selectBox->setPosition(Plate2Screen(row, col));
        selectBox->setVisible(true);
    
        _selectedId = clickedId;//记录下棋子id
    }
    
    void LayerGameMain::moveStone(Touch *pTouch)
    {
        Point ptClicked = pTouch->getLocation();
        int row, col;
        if (!Screen2Plate(ptClicked, row, col))//获取触点的行列
        {
            return;
        }
        int clickedId = getStoneIdFromRowCol(row, col);//获取触点上的棋子id,没有棋子为-1
        if (!canMove(_selectedId, row, col, clickedId))
        {
            return;
        }
    
        Point pt = Plate2Screen(row, col);
        MoveTo *to = MoveTo::create(0.3, pt);
        Stone *selectedStone = getStoneFromArrById(_selectedId);
        selectedStone->runAction(to);
        selectedStone->_row = row;
        selectedStone->_col = col;
    
    }
    
    int LayerGameMain::getStoneCount(int row1, int col1, int row2, int col2)
    {
        int ret = 0;//记录棋子的个数
        if (row1 != row2 && col1 != col2) return -1;//行列都不同,没有直线
        if (row1 == row2 && col1 == col2) return -1;//行列都相同,两点相同,不需要移动
    
        if (row1 == row2)//列相同
        {
            int mincol = col1 < col2 ? col1 : col2;//获取两点间较小的行
            int maxcol = col1 > col2 ? col1 : col2;//获取两点间较大的行
            for (int col = mincol+1; col < maxcol; col++)//遍历两点间所有点
            {
                if (getStoneIdFromRowCol(row1, col) > 0)//如果存在棋子,ret棋子计数器+1
                {
                    ++ret;
                }
            }
        }
        else if(col1 == col2)
        {
            int minrow = row1 < row2 ? row1 : row2;//获取两点间较小的列
            int maxrow = row1 > row2 ? row1 : row2;//获取两点间较大的列
            for (int row = minrow + 1; row < maxrow; row++)//遍历两点间所有点
            {
                if (getStoneIdFromRowCol(row, col1) > 0)//如果存在棋子,ret棋子计数器+1
                {
                    ++ret;
                }
            }
        }
    
        return ret;
    }
    
    bool LayerGameMain::canMove(int moveid, int row, int col, int killid)
    {
        Stone *stone = getStoneFromArrById(moveid);
        switch (stone->_type)
        {
            case Stone::CHE:
                return canMoveChe(moveid, row, col);
                break;
            case Stone::JIANG:
                return canMoveJiang(moveid, row, col, killid);
                break;
            case Stone::BING:
                return canMoveBing(moveid, row, col);
                break;
            case Stone::PAO:
                return canMovePao(moveid, row, col, killid);
                break;
            case Stone::MA:
                return canMoveMa(moveid, row, col);
                break;
            case Stone::XIANG:
                return canMoveXiang(moveid, row, col);
                break;
            case Stone::SHI:
                return canMoveShi(moveid, row, col);
                break;
            default:
                break;
        }
        return false;
    }
    bool LayerGameMain::canMoveChe(int moveid, int row, int col)
    {
        //车只有在两点间不存在棋子时才能走
        Stone *che = getStoneFromArrById(moveid);
        if (getStoneCount(che->_row, che->_col, row, col)>0)
        {
            false;
        }
        return true;
    }
    bool LayerGameMain::canMovePao(int moveid, int row, int col, int killid)
    {
        //炮可以行走只有两种情况:1.两点间没有棋子。2.两点间存在一个棋子,并且目的点也存在棋子。
        Stone *pao = getStoneFromArrById(moveid);
        if (killid == -1)
        {
            return getStoneCount(pao->_row, pao->_col, row, col) == 0;
        }
    
        return getStoneCount(pao->_row, pao->_col, row, col) == 1;
    }
    bool LayerGameMain::canMoveBing(int moveid, int row, int col)
    {
        //兵行走规则:1.只能走一格。2.不能回退。3.过河前只能直走
        Stone *bing = getStoneFromArrById(moveid);
        int dRow = abs(bing->_row - row);
        int dCol = abs(bing->_col - col);
        int d = dRow * 10 + dCol;//此处类似标记效果,10等价于(1,0),1代表可移动,第一位说明可以左右移动一格,第二位说明可以上下移动一格
        if (d != 1 && d != 10)
        {
            return false;
        }
    
        if (bing->_red)
        {
            //兵不能回退
            if (row < bing->_row)
            {
                return false;
            }
            //兵过河前不能左右移动
            if (bing->_row <= 4 && bing->_row == row)
            {
                return false;
            }
        }
        else
        {
            if (row > bing->_row)
            {
                return false;
            }
            if (bing->_row >= 5 && bing->_row == row)
            {
                return false;
            }
        }
    
        return true;
    }
    bool LayerGameMain::canMoveJiang(int moveid, int row, int col, int killid)
    {
        //将规则:1.一次只能移动一格。2.不能出九宫格。3.将帅照面时可以直接对杀。
        Stone *jiang = getStoneFromArrById(moveid);
        //将照面杀
        if (killid != -1)
        {
            Stone *kill = getStoneFromArrById(killid);
            if (kill->_type == Stone::JIANG)
            {
                return canMoveChe(moveid, row, col);//复用车的移动方法,减少代码量
            }
        }
        int dRow = abs(jiang->_row - row);
        int dCol = abs(jiang->_col - col);
        int d = dRow * 10 + dCol;//此处类似标记效果,10等价于(1,0),1代表可移动,第一位说明可以左右移动一格,第二位说明可以上下移动一格
        if (d != 1 && d != 10)
        {
            return false;
        }
        //将不能左右出九宫
        if (col < 3 || col > 5)
        {
            return false;
        }
        //将不能上下出九宫
        if (jiang->_red)
        {
            if (row > 2 || row <0)
            {
                return false;
            }
        }
        else
        {
            if (row < 7 || row >9)
            {
                return false;
            }
        }
        return true;
    }
    bool LayerGameMain::canMoveMa(int moveid, int row, int col)
    {
        //马规则:1.马走日。2.卡住马脚不能移动。
        Stone *ma = getStoneFromArrById(moveid);
        int dRow = abs(ma->_row - row);
        int dCol = abs(ma->_col - col);
        int d = dRow * 10 + dCol;
        //12为横日,21为竖日,类似于(1,2)(2,1)做法
        if (d == 12 || d == 21)
        {
            int cRow, cCol;
            if (d == 12)
            {
                //蹩脚点
                cCol = (col + ma->_col) / 2;
                cRow = ma->_row;
            }
            else
            {
                cCol = ma->_col;
                cRow = (ma->_row + row) / 2;
            }
            //获取蹩脚点后判断蹩脚点是否有棋子
            if (getStoneIdFromRowCol(cRow, cCol) == -1)
            {
                return true;
            }
        }
        return false;
    }
    bool LayerGameMain::canMoveXiang(int moveid, int row, int col)
    {
        //象规则:1.象走田。2.被卡象眼不能移动。3.象不能过河
        Stone *xiang = getStoneFromArrById(moveid);
        int dRow = abs(xiang->_row - row);
        int dCol = abs(xiang->_col - col);
        int d = dRow * 10 + dCol;
        if (d != 22)
        {
            return false;
        }
    
        int cRow, cCol;
        cRow = (row + xiang->_row) / 2;
        cCol = (col + xiang->_col) / 2;
        if (getStoneIdFromRowCol(cRow, cCol) != -1)//卡主香烟不能移动
        {
            return false;
        }
    
        if (xiang->_red)
        {
            if (row > 4) return false;
        }
        else
        {
            if (row < 5) return false;
        }
    
        return true;
    }
    bool LayerGameMain::canMoveShi(int moveid, int row, int col)
    {
        //士规则:1.一次斜着走一格。2.不能出九宫格
        Stone *shi = getStoneFromArrById(moveid);
        int dRow = abs(shi->_row - row);
        int dCol = abs(shi->_col - col);
        int d = dRow * 10 + dCol;
        //只能斜线走
        if (d != 11)
        {
            return false;
        }
        //不能两侧走出九宫
        if (col<3 || col>5)
        {
            return false;
        }
        //不能上下走出九宫
        if (shi->_red)
        {
            if (row>2 || row<0)
            {
                return false;
            }
        }
        else
        {
            if (row<7 || row>9)
            {
                return false;
            }
        }
    
        return true;
    }
    

    相关文章

      网友评论

        本文标题:cocos2d-x3.14中国象棋AI(五)移动棋子1

        本文链接:https://www.haomeiwen.com/subject/ytxqqxtx.html