象棋和五子棋的AI开发(三)

三、象棋AI算法

3.1 走法

相对于五子棋,象棋的走法是更复杂的。在走法里面,要说的东西不多,我直接贴代码:

protected override List<Step> GenerateAllMove(ChessColor c)
{
    List<Step> moves = new List<Step>();

    for (int i = 0; i < Row; i++)
    {
        for (int j = 0; j < Col; j++)
        {
            if (board[i, j].type != ChessType.空 && board[i, j].color == c)
            {
                switch (board[i, j].type)
                {
                    case ChessType.卒:
                        GenerateMove卒(i, j, c, moves);
                        break;
                    case ChessType.士:
                        GenerateMove士(i, j, c, moves);
                        break;
                    case ChessType.将:
                        GenerateMove将(i, j, c, moves);
                        break;
                    case ChessType.象:
                        GenerateMove象(i, j, c, moves);
                        break;
                    case ChessType.马:
                        GenerateMove马(i, j, c, moves);
                        break;
                    case ChessType.车:
                        GenerateMove车(i, j, c, moves);
                        break;
                    case ChessType.炮:
                        GenerateMove炮(i, j, c, moves);
                        break;
                }
            }
        }
    }

    return moves;
}

private void GenerateMove卒(int row, int col, ChessColor c, List<Step> moves)
{
    if (c == ChessColor.红)
    {
        if (row < 5)
        {
            AddStep(moves, new Step(row, col, row + 1, col), c);
        }
        else
        {
            AddStep(moves, new Step(row, col, row + 1, col), c);
            AddStep(moves, new Step(row, col, row, col + 1), c);
            AddStep(moves, new Step(row, col, row, col - 1), c);
        }
    }
    else
    {
        if (row >= 5)
        {
            AddStep(moves, new Step(row, col, row - 1, col), c);
        }
        else
        {
            AddStep(moves, new Step(row, col, row - 1, col), c);
            AddStep(moves, new Step(row, col, row, col + 1), c);
            AddStep(moves, new Step(row, col, row, col - 1), c);
        }
    }
}

private void GenerateMove士(int row, int col, ChessColor c, List<Step> moves)
{
    int row1 = row + 1;
    int row_1 = row - 1;
    int col1 = col + 1;
    int col_1 = col - 1;

    Step[] steps = new Step[] {
        new Step(row,col,row1,col1),
        new Step(row,col,row1,col_1),
        new Step(row,col,row_1,col1),
        new Step(row,col,row_1,col_1)
    };

    if (c == ChessColor.红)
    {
        for (int i = 0; i < steps.Length; i++)
        {
            if (steps[i].ToRow < 0 || steps[i].ToRow > 2)
            {
                continue;
            }
            if (steps[i].ToCol < 3 || steps[i].ToCol > 5)
            {
                continue;
            }

            AddStep(moves, steps[i], c);
        }
    }
    else
    {
        for (int i = 0; i < steps.Length; i++)
        {
            if (steps[i].ToRow < 7 || steps[i].ToRow > 9)
            {
                continue;
            }
            if (steps[i].ToCol < 3 || steps[i].ToCol > 5)
            {
                continue;
            }

            AddStep(moves, steps[i], c);
        }
    }
}

private void GenerateMove将(int row, int col, ChessColor c, List<Step> moves)
{
    Step[] steps = new Step[] {
        new Step(row,col, row + 1,col),
        new Step(row,col, row - 1,col),
        new Step(row,col,row, col + 1),
        new Step(row,col,row, col - 1)
    };

    if (c == ChessColor.红)
    {
        for (int i = 0; i < steps.Length; i++)
        {
            if (steps[i].ToRow < 0 || steps[i].ToRow > 2)
            {
                continue;
            }
            if (steps[i].ToCol < 3 || steps[i].ToCol > 5)
            {
                continue;
            }

            AddStep(moves, steps[i], c);
        }

        bool face_to_face = false;
        int start = row + 1;
        for (; start < 10; start++)
        {
            if (board[start, col].type == ChessType.将)
            {
                face_to_face = true;
                break;
            }
            else if (board[start, col].type != ChessType.空)
            {
                break;
            }
        }
        if (face_to_face)
        {
            AddStep(moves, new Step(row, col, start, col), c);
        }
    }
    else if (c == ChessColor.黑)
    {
        for (int i = 0; i < steps.Length; i++)
        {
            if (steps[i].ToRow < 7 || steps[i].ToRow > 9)
            {
                continue;
            }
            if (steps[i].ToCol < 3 || steps[i].ToCol > 5)
            {
                continue;
            }

            AddStep(moves, steps[i], c);
        }

        bool face_to_face = false;
        int start = row - 1;
        for (; start >= 0; start--)
        {
            if (board[start, col].type == ChessType.将)
            {
                face_to_face = true;
                break;
            }
            else if (board[start, col].type != ChessType.空)
            {
                break;
            }
        }
        if (face_to_face)
        {
            AddStep(moves, new Step(row, col, start, col), c);
        }
    }
}

private void GenerateMove象(int row, int col, ChessColor c, List<Step> moves)
{
    int row1 = row + 1;
    int row2 = row + 2;
    int row_1 = row - 1;
    int row_2 = row - 2;
    int col1 = col + 1;
    int col2 = col + 2;
    int col_1 = col - 1;
    int col_2 = col - 2;

    if (c == ChessColor.红)
    {
        if (row_1 >= 0 && col_1 >= 0 && board[row_1, col_1].type == ChessType.空)
        {
            AddStep(moves, new Step(row, col, row_2, col_2), c);
        }
        if (row_1 >= 0 && col1 <= 8 && board[row_1, col1].type == ChessType.空)
        {
            AddStep(moves, new Step(row, col, row_2, col2), c);
        }
        if (row1 <= 4 && col_1 >= 0 && board[row1, col_1].type == ChessType.空)
        {
            AddStep(moves, new Step(row, col, row2, col_2), c);
        }
        if (row1 <= 4 && col1 <= 8 && board[row1, col1].type == ChessType.空)
        {
            AddStep(moves, new Step(row, col, row2, col2), c);
        }
    }
    else
    {
        if (row_1 >= 5 && col_1 >= 0 && board[row_1, col_1].type == ChessType.空)
        {
            AddStep(moves, new Step(row, col, row_2, col_2), c);
        }
        if (row_1 >= 5 && col1 <= 8 && board[row_1, col1].type == ChessType.空)
        {
            AddStep(moves, new Step(row, col, row_2, col2), c);
        }
        if (row1 <= 9 && col_1 >= 0 && board[row1, col_1].type == ChessType.空)
        {
            AddStep(moves, new Step(row, col, row2, col_2), c);
        }
        if (row1 <= 9 && col1 <= 8 && board[row1, col1].type == ChessType.空)
        {
            AddStep(moves, new Step(row, col, row2, col2), c);
        }
    }
}

private void GenerateMove马(int row, int col, ChessColor c, List<Step> moves)
{
    int row1 = row + 1;
    int row2 = row + 2;
    int row_1 = row - 1;
    int row_2 = row - 2;
    int col1 = col + 1;
    int col2 = col + 2;
    int col_1 = col - 1;
    int col_2 = col - 2;

    if (row_1 >= 0 && board[row_1, col].type == ChessType.空)
    {
        AddStep(moves, new Step(row, col, row_2, col_1), c);
        AddStep(moves, new Step(row, col, row_2, col1), c);
    }
    if (row1 < 10 && board[row1, col].type == ChessType.空)
    {
        AddStep(moves, new Step(row, col, row2, col_1), c);
        AddStep(moves, new Step(row, col, row2, col1), c);
    }
    if (col_1 >= 0 && board[row, col_1].type == ChessType.空)
    {
        AddStep(moves, new Step(row, col, row_1, col_2), c);
        AddStep(moves, new Step(row, col, row1, col_2), c);
    }
    if (col1 < 9 && board[row, col1].type == ChessType.空)
    {
        AddStep(moves, new Step(row, col, row_1, col2), c);
        AddStep(moves, new Step(row, col, row1, col2), c);
    }
}

private void GenerateMove车(int row, int col, ChessColor c, List<Step> moves)
{
    int temp_row = row;
    while (temp_row > 0)
    {
        temp_row--;
        if (board[temp_row, col].type == ChessType.空)
        {
            AddStep(moves, new Step(row, col, temp_row, col), c);
        }
        else if (board[temp_row, col].color == c)
        {
            break;
        }
        else
        {
            AddStep(moves, new Step(row, col, temp_row, col), c);
            break;
        }
    }

    temp_row = row;
    while (temp_row < 9)
    {
        temp_row++;
        if (board[temp_row, col].type == ChessType.空)
        {
            AddStep(moves, new Step(row, col, temp_row, col), c);
        }
        else if (board[temp_row, col].color == c)
        {
            break;
        }
        else
        {
            AddStep(moves, new Step(row, col, temp_row, col), c);
            break;
        }
    }

    int temp_col = col;
    while (temp_col > 0)
    {
        temp_col--;
        if (board[row, temp_col].type == ChessType.空)
        {
            AddStep(moves, new Step(row, col, row, temp_col), c);
        }
        else if (board[row, temp_col].color == c)
        {
            break;
        }
        else
        {
            AddStep(moves, new Step(row, col, row, temp_col), c);
            break;
        }
    }

    temp_col = col;
    while (temp_col < 8)
    {
        temp_col++;
        if (board[row, temp_col].type == ChessType.空)
        {
            AddStep(moves, new Step(row, col, row, temp_col), c);
        }
        else if (board[row, temp_col].color == c)
        {
            break;
        }
        else
        {
            AddStep(moves, new Step(row, col, row, temp_col), c);
            break;
        }
    }
}

private void GenerateMove炮(int row, int col, ChessColor c, List<Step> moves)
{
    int temp_row = row;
    bool hit = false;
    while (temp_row > 0)
    {
        temp_row--;
        if (board[temp_row, col].type == ChessType.空)
        {
            if (!hit)
            {
                AddStep(moves, new Step(row, col, temp_row, col), c);
            }
        }
        else if (board[temp_row, col].color == c)
        {
            if (!hit)
            {
                hit = true;
            }
            else
            {
                break;
            }
        }
        else
        {
            if (!hit)
            {
                hit = true;
            }
            else
            {
                AddStep(moves, new Step(row, col, temp_row, col), c);
                break;
            }
        }
    }

    temp_row = row;
    hit = false;
    while (temp_row < 9)
    {
        temp_row++;
        if (board[temp_row, col].type == ChessType.空)
        {
            if (!hit)
            {
                AddStep(moves, new Step(row, col, temp_row, col), c);
            }
        }
        else if (board[temp_row, col].color == c)
        {
            if (!hit)
            {
                hit = true;
            }
            else
            {
                break;
            }
        }
        else
        {
            if (!hit)
            {
                hit = true;
            }
            else
            {
                AddStep(moves, new Step(row, col, temp_row, col), c);
                break;
            }
        }
    }

    int temp_col = col;
    hit = false;
    while (temp_col > 0)
    {
        temp_col--;
        if (board[row, temp_col].type == ChessType.空)
        {
            if (!hit)
            {
                AddStep(moves, new Step(row, col, row, temp_col), c);
            }
        }
        else if (board[row, temp_col].color == c)
        {
            if (!hit)
            {
                hit = true;
            }
            else
            {
                break;
            }
        }
        else
        {
            if (!hit)
            {
                hit = true;
            }
            else
            {
                AddStep(moves, new Step(row, col, row, temp_col), c);
                break;
            }
        }
    }

    temp_col = col;
    hit = false;
    while (temp_col < 8)
    {
        temp_col++;
        if (board[row, temp_col].type == ChessType.空)
        {
            if (!hit)
            {
                AddStep(moves, new Step(row, col, row, temp_col), c);
            }
        }
        else if (board[row, temp_col].color == c)
        {
            if (!hit)
            {
                hit = true;
            }
            else
            {
                break;
            }
        }
        else
        {
            if (!hit)
            {
                hit = true;
            }
            else
            {
                AddStep(moves, new Step(row, col, row, temp_col), c);
                break;
            }
        }
    }
}

private int AdjustRow(ChessColor c, int row)
{
    if (c == ChessColor.黑)
    {
        return Row - 1 - row;
    }
    return row;
}

private int AdjustCol(ChessColor c, int col)
{
    if (c == ChessColor.黑)
    {
        return Col - 1 - col;
    }
    return col;
}

private int AdjustChange(ChessColor c)
{
    if (c == ChessColor.黑)
    {
        return -1;
    }
    return 1;
}

private void AddStep(List<Step> moves, Step step, ChessColor c)
{
    if (step.ToRow < 0 || step.ToRow >= Row)
    {
        return;
    }
    if (step.ToCol < 0 || step.ToCol >= Col)
    {
        return;
    }
    if (board[step.ToRow, step.ToCol].type != ChessType.空 && board[step.ToRow, step.ToCol].color == c)
    {
        return;
    }

    moves.Add(step);
}

3.2 走法排序

走法排序的目的是使用历史启发算法。我们先来看一下代码:

protected override void SortMove(List<Step> steps)
{
    for (int i = steps.Count - 1; i >= 0; i--)
    {
        Step step = steps[i];

        //杀子优先
        switch (board[step.ToRow, step.ToCol].type)
        {
            case ChessType.卒:
                step.value = VALUE卒 * (1 << AlgorithmDepth) / 10;
                break;
            case ChessType.士:
                step.value = VALUE士 * (1 << AlgorithmDepth) / 10;
                break;
            case ChessType.将:
                step.value = VALUE将 * (1 << AlgorithmDepth) / 10;
                break;
            case ChessType.炮:
                step.value = VALUE炮 * (1 << AlgorithmDepth) / 10;
                break;
            case ChessType.象:
                step.value = VALUE象 * (1 << AlgorithmDepth) / 10;
                break;
            case ChessType.车:
                step.value = VALUE车 * (1 << AlgorithmDepth) / 10;
                break;
            case ChessType.马:
                step.value = VALUE马 * (1 << AlgorithmDepth) / 10;
                break;
            default:
                step.value = 0;
                break;
        }

        //历史启发
        int from_chess = GetStepNum(step);
        if (history_score.ContainsKey(from_chess))
        {
            step.value += history_score[from_chess];
        }
    }

    steps.Sort();
}

排在前面的走法会被优先考虑,我们一般使用之前出现剪枝的走法。根据本人的经验,杀子的走法一般都是比较好的走法,所以就同时考虑了杀子优先。特别是在开局的时候,杀子优先作用更大。

3.3 估值算法

先看代码:

protected override int Evaluation(ChessColor c, Step[] pre_steps)
{
    int score = 0;
    for (int i = 0; i < Row; i++)
    {
        for (int j = 0; j < Col; j++)
        {
            Chess chess = board[i, j];
            if (chess.type != ChessType.空)
            {
                int temp_score = 0;
                List<Step> moves = new List<Step>();
                switch (chess.type)
                {
                    case ChessType.卒:
                        temp_score = VALUE卒;
                        GenerateMove卒(i, j, chess.color, moves);
                        temp_score += moves.Count * STEP卒;
                        break;
                    case ChessType.炮:
                        temp_score = VALUE炮;
                        GenerateMove炮(i, j, chess.color, moves);
                        temp_score += moves.Count * STEP炮;
                        break;
                    case ChessType.马:
                        temp_score = VALUE马;
                        GenerateMove马(i, j, chess.color, moves);
                        temp_score += moves.Count * STEP马;
                        break;
                    case ChessType.士:
                        temp_score = VALUE士;
                        GenerateMove士(i, j, chess.color, moves);
                        temp_score += moves.Count * STEP士;
                        break;
                    case ChessType.象:
                        temp_score = VALUE象;
                        GenerateMove象(i, j, chess.color, moves);
                        temp_score += moves.Count * STEP象;
                        break;
                    case ChessType.车:
                        temp_score = VALUE车;
                        GenerateMove车(i, j, chess.color, moves);
                        temp_score += moves.Count * STEP车;
                        break;
                    case ChessType.将:
                        temp_score = VALUE将;
                        break;
                }

                if (chess.color == c)
                {
                    score += temp_score;
                }
                else
                {
                    score -= temp_score;
                }
            }
        }
    }

    return score;
}

在《PC游戏编程(人机博弈)》一书里,估值考虑了三种情况。一是每种棋子的价值,二是每种棋子的灵活性,三是棋子是否受到了威胁。前面两种情况计算还是比较快的,第三种情况则较慢(测试得出)。我认为,其实这一局的收到威胁,就是下一局的杀子。多一层,其实就是考虑了威胁,所以不必考虑威胁这种情况。上面的代码就是只考虑棋子价值和灵活性。在这样的情况下,我的电脑可以运行6层,而如果加上威胁检测,就只能运行5层了。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值