QT象棋练习(7)一步人工智能

为实现人工智能走棋,重新建立一个类,从board派生。board是棋盘类,这个是基本的象棋走法。

在前面已经提到,叫singlegame。这个类从board派生。

#include "Board.h"

class SingleGame : public Board

在人机对战中,玩家下了后,轮到机器下,但是singlegame如何获取玩家已经走了一步这个事情?也就是说当父类board玩家走了一步后,singlegame也要知道。这里用虚函数来记录。在虚函数void mouseReleaseEvent(QMouseEvent *ev);中调用了click函数。这个click函数表示在棋盘上点击的点。

void Board::mouseReleaseEvent(QMouseEvent *ev)
{
    if(ev->button() != Qt::LeftButton)
    {
        return;
    }

    click(ev->pos());
}

在这个函数中,也调用了另外一个click函数。这个函数是表示点击的棋子和他的坐标点。

void Board::click(QPoint pt)
{
    int row, col;
    bool bClicked = getClickRowCol(pt, row, col);
    if(!bClicked) return;

    int id = getStoneId(row, col);
    click(id, row, col);
}

这个函数是表示点击的棋子id和它的坐标点。把这个函数设置为虚函数。

virtual void click(int id, int row, int col);

假如将这个函数在子类中重载的话,以后调用的就这个子类的函数,而不是上面那个函数。在子类singlegame中重写这个函数,在这个函数先就让调用父类中的函数,不做修改。这样的话跟以前没区别。

Board::click(id, row, col);

现在实现该函数,虚函数click是人点击后才进来的函数。在此说明一下,人走的是红棋,电脑走的是黑棋。这个函数逻辑是这样的,如果玩家走完后,就进入这个函数,然后是电脑走,电脑走用一个函数Step* getBestMove()实现。

现在实现函数Step* getBestMove();

Step* getBestMove();

该函数实现需要考虑几个步骤:

1、看看可以走哪几步。

2、每一步都尝试走一遍。

3、评估每一步的结果。

4、去最好的结果。

实现每一步,第一个,看看哪一几步可以走。写一个函数实现,并用一个数组保存步骤。

void getAllPossibleMove(QVector<Step*>& steps);

这个函数其实就是遍历那些棋子可以走。 注意的是棋子在被吃掉后,便不能再走了。

void SingleGame::getAllPossibleMove(QVector<Step *> &steps)
{
    int min, max;
    if(this->_bRedTurn)
    {
        min = 0, max = 16;   //黑棋
    }
    else
    {
        min = 16, max = 32;  //红棋
    }

    for(int i=min;i<max; i++)
    {
        if(this->_s[i]._dead) continue;  //如果该棋子死了,继续
        for(int row = 0; row<=9; ++row)
        {
            for(int col=0; col<=8; ++col)
            {
                int killid = this->getStoneId(row, col);  //看遍历的这个行列有没有棋子
                if(sameColor(i, killid)) continue;  //看是否颜色相同,也就是不能吃自己的棋子

                if(canMove(i, killid, row, col))
                {
                    saveStep(i, killid, row, col, steps);  //如果能走,就把这步放入数组中去
                }
            }
        }
    }
}

函数saveStep()是步数放入数组中去。实现如下。

void Board::saveStep(int moveid, int killid, int row, int col, QVector<Step*>& steps)
{
    GetRowCol(row1, col1, moveid);
    Step* step = new Step;
    step->_colFrom = col1;
    step->_colTo = col;
    step->_rowFrom = row1;
    step->_rowTo = row;
    step->_moveid = moveid;
    step->_killid = killid;

    steps.append(step);
}

实现剩下的步骤,尝试以下每一步的走法和评估每一步。也就是遍历数组中的每一步,然后给每一步打个分数。然后取最高分数的步骤。


Step* SingleGame::getBestMove()
{

    // 1.看看有那些步骤可以走
    QVector<Step*> steps;
    getAllPossibleMove(steps);

    // 2.试着走一下
    // 3.评估走的结果
    int maxScore = -100000;
    Step* ret = NULL;
    while(steps.count())
    {
        Step* step = steps.back();
        steps.removeLast();

        fakeMove(step);  //假象实现该步,不在棋盘上显示,就好像是在心中思考
        int score = getMinScore(_level-1, maxScore);
        unfakeMove(step);

        if(score > maxScore)
        {
            maxScore = score;
            if(ret) delete ret;
            ret = step;
        }
        else
        {
            delete step;
        }
    }

    // 4.取最好的结果作为参考
    return ret;
}

一步的人工智能的思路已经实现,但是目前还有以下几个函数没实现。

    void fakeMove(Step* step);
    void unfakeMove(Step* step);
    int calcScore();

实现第一个函数,这个函数用 killStone(step->_killid);实现,直接将id传进去。这个函数以前已经实现。

void Board::killStone(int id)
{
    if(id==-1) return;
    _s[id]._dead = true;
}

然后将棋子移过去。函数完整如下。

void SingleGame::fakeMove(Step* step)
{
    killStone(step->_killid);
    moveStone(step->_moveid, step->_rowTo, step->_colTo);
}
void Board::moveStone(int moveid, int row, int col)
{
    _s[moveid]._row = row;
    _s[moveid]._col = col;

    _bRedTurn = !_bRedTurn;
}

将棋子移过去后,还需要将他移回来,因为这个是假象的步骤,不是真正的走棋。先复活棋子,在移回来。

void SingleGame::unfakeMove(Step* step)
{
    reliveStone(step->_killid);
    moveStone(step->_moveid, step->_rowFrom, step->_colFrom);
}
void Board::reliveStone(int id)
{
    if(id==-1) return;
    _s[id]._dead = false;
}

最后一个函数,评价局面分。如何评价步骤的优劣?我们给每个棋子给以分数。然后将每一方的棋子分数加起来,用黑色棋子的总分数减去红棋的总分数。

     //enum TYPE{CHE, MA, PAO, BING, JIANG, SHI, XIANG};
    static int chessScore[] = {100, 50, 50, 20, 1500, 10, 10};

定义双方的总分数,

    int redTotalScore = 0;
    int blackTotalScore = 0;

计算双方的分数。

    // 黑棋分的总数 - 红棋分的总数
    for(int i=0; i<16; ++i)
    {
        if(_s[i]._dead) continue;
        redTotalScore += chessScore[_s[i]._type];
    }
    for(int i=16; i<32; ++i)
    {
        if(_s[i]._dead) continue;
        blackTotalScore += chessScore[_s[i]._type];
    }

    return blackTotalScore - redTotalScore;

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值