人机对战-黑白棋

先大致了解一下黑白棋:

规则

如果玩家在棋盘上没有地方可以下子,则该玩家对手可以连下。双方都没有棋子可以下时棋局结束,以棋子数目来计算胜负,棋子多的一方获胜。

在棋盘还没有下满时,如果一方的棋子已经被对方吃光,则棋局也结束。将对手棋子吃光的一方获胜。

翻转棋类似于棋盘游戏“奥赛罗 (Othello)”,是一种得分会戏剧性变化并且需要长时间思考的策略性游戏。

翻转棋的棋盘上有 64 个可以放置黑白棋子的方格(类似于国际象棋和跳棋)。游戏的目标是使棋盘上自己颜色的棋子数超过对手的棋子数。

该游戏非常复杂,其名称就暗示着结果的好坏可能会迅速变化。

当游戏双方都不能再按规则落子时,游戏就结束了。通常,游戏结束时棋盘上会摆满了棋子。结束时谁的棋子最多谁就是赢家。

玩法

每个“翻转棋”游戏开始时,棋盘上已经交叉放好了四颗棋子。其中两颗是黑棋,另两颗是白棋。黑棋总是先走。

当您的棋子在某一直线方向包围了对手的棋子时,就可以翻转这些棋子的颜色,使它们成为您方的颜色。例如,如果您执黑棋,并且看到在一排白棋的某一端是一颗黑棋,那么当您将一颗黑棋放在这一排的另一端时,所有的白棋都将翻转并变为黑棋!

所有的直线方向均有效:水平、垂直和斜线方向。

走棋的唯一规则是只能走包围并翻转对手的棋子。每一回合都必须至少翻转一颗对手的棋子。

按规则不能再走棋时,这一回合弃权。这一步的行棋权将被交给对方。

由以上可知,在做黑白棋人机对战时,AI要遵守游戏规则。根据

感知(Sense)→思考(Think)→行动(Act)

这个基本架构去设计AI

感知玩家下的棋子位置;

思考我方下子后,增加多少分,玩家接下来走的位置,会减少我方多少分,以相差最高为标准,确定下棋位置;

行动落子;

根据以上分析,编写程序,代码如下:

#include <stdio.h>
//显示棋盘上棋子的状态
void Output(char chessboard[][8])
{
    int row, col;
    printf("\n   ");
    //输出列标号
    for (col = 0; col < 8; col++)
    {
        printf("  %c ", ‘A‘ + col);
    }
    printf("\n");
    //输出项部横线
    printf("  ┌");
    //输出一行
    for (col = 0; col < 7; col++)
    {
        printf("─┬");
    }
    printf("─┐\n");
    for (row = 0; row < 8; row++)
    {
        //输出行号
        printf("%2d│", row + 1);
        //输出棋盘各单元格中棋子的状态
        for (col = 0; col < 8; col++)
        {
            if (chessboard[row][col] == 1)//白棋
            {
                printf("○│");
            }
            else if (chessboard[row][col] == -1)//黑棋
            {
                printf("●│");
            }
            else//未下子处
            {
                printf("  │");
            }
        }
        printf("\n");
        if (row < 8 - 1)
        {
            printf("  ├");  //输出交叉线
            //输出一行
            for (col = 0; col < 8 - 1; col++)
            {
                printf("─┼");
            }
            printf("─┤\n");
        }
    }
    printf("  └");
    //最后一行的横线
    for (col = 0; col < 8 - 1; col++)
    {
        printf("─┴");
    }
    printf("─┘\n");
}
//检查某一方是否还有下子的地方
int Check(char chessboard[][8], int isDown[][8], char player)
{
    int rowdelta, coldelta, row, col, x, y = 0;
    int iStep = 0;
    char opponent = (player == 1) ? -1 : 1; //对方棋子
    char myplayer = -1 * opponent;  //我方棋子
    //将isDown数组全部清0
    for (row = 0; row < 8; row++)
    {
        for (col = 0; col < 8; col++)
        {
            isDown[row][col] = 0;
        }
    }
    //循环判断棋盘中哪些单元格可以下子
    for (row = 0; row < 8; row++)
    {
        for (col = 0; col < 8; col++)
        {
            //若棋盘上对应位置不为空(表示已经有子)
            if (chessboard[row][col] != 0)
            {
                continue;//继续处理下一个单元格
            }
            //循环检查上下行
            for (rowdelta = -1; rowdelta <= 1; rowdelta++)
            {
                //循环检查左右列
                for (coldelta = -1; coldelta <= 1; coldelta++)
                {
                    //检查若坐标超过棋盘 或为当前单元格
                    if (row + rowdelta < 0 || row + rowdelta >= 8
                        || col + coldelta < 0 || col + coldelta >= 8
                        || (rowdelta == 0 && coldelta == 0))
                    {
                        continue;   //继续循环
                    }
                    //若(row,col)四周有对手下的子
                    if (chessboard[row + rowdelta][col + coldelta] == opponent)
                    {
                        //以对手下子位置为坐标
                        x = row + rowdelta;
                        y = col + coldelta;
                        //对对手下子为起始点,向四周查找自己方的棋子,以攻击对方棋子
                        while(1)
                        {
                            //对手下子的四周坐标
                            x += rowdelta;
                            y += coldelta;
                            //超过棋盘
                            if (x < 0 || x >= 8 || y < 0 || y >= 8)
                            {
                                break;  //退出循环
                            }
                            //若对应位置为空
                            if (chessboard[x][y] == 0)
                            {
                                break;
                            }
                            //若对应位置下的子是当前棋手的
                            if (chessboard[x][y] == myplayer)
                            {
                                //设置移动数组中对应位置为1 (该位置可下子,形成向对手进攻的棋形)
                                isDown[row][col] = 1;
                                iStep++;    //累加可下子的位置数量
                                break;
                            }
                        }
                    }
                }
            }
        }
    }
    //返回可下的位置数量(若返回值为0,表示没地方可下)
    return iStep;
}
//在指定位置下子
void PlayStep(char chessboard[][8], int row, int col, char player)
{
    int rowdelta = 0;
    int coldelta = 0;
    int x = 0;
    int y = 0;
    char opponent = (player == 1) ? -1 : 1; //对方棋子
    char myplayer = -1 * opponent;  //我方棋子
    chessboard[row][col] = myplayer;    //保存所下的棋子
    //检查所下子四周的棋子
    for (rowdelta = -1; rowdelta <= 1; rowdelta++)
    {
        for (coldelta = -1; coldelta <= 1; coldelta++)
        {
            //若坐标超过棋盘界限
            if (row + rowdelta < 0 || row + rowdelta >= 8 || col + coldelta < 0
                || col + coldelta >= 8 || (rowdelta == 0 && coldelta == 0))
            {
                continue;   //继续下一位置
            }
            //若该位置是对手的棋子
            if (chessboard[row + rowdelta][col + coldelta] == opponent)
            {
                //以对手棋为坐标
                x = row + rowdelta;
                y = col + coldelta;
                //在对手棋子四周寻找我方棋子
                while(1)
                {
                    x += rowdelta;
                    y += coldelta;
                    //若坐标超过棋盘
                    if (x < 0 || x >= 8 || y < 0 || y >= 8)
                    {
                        break;  //退出循环
                    }
                    //若对应位置为空
                    if (chessboard[x][y] == 0)
                    {
                        break;  //退出循环
                    }
                    //若对应位置是我方模子
                    if (chessboard[x][y] == myplayer)
                    {
                        //循环处理
                        while (chessboard[x -= rowdelta][y -= coldelta] == opponent)
                        {
                            //将中间的棋子都变成我方棋子
                            chessboard[x][y] = myplayer;
                        }
                        break;  //退出循环
                    }
                }
            }
        }
    }
}
//获取分数
int GetMaxScore(char chessboard[][8], char player)
{
    int Score, row, col;
    char opponent = (player == 1) ? -1 : 1; //对方棋子
    char myplayer=-1*opponent;
    for (row = 0; row < 8; row++)   //循环
    {
        for (col = 0; col < 8; col++)
        {
            //若棋盘对应位置是对手下的棋子,从总分中减1
            Score -= chessboard[row][col] == opponent;
            //若棋盘对应位置是我方的棋子,总分中加1分
            Score += chessboard[row][col] == myplayer;
        }
    }
    return Score;//返回分数
}
//获取最佳下子位置
int BestPlay(char chessboard[][8], int isDown[][8], char player)
{
    int row, col, i, j;
    //定义一个临时数组
    char chessboard1[8][8] = { 0 };
    int MaxScore = 0;   //保存最高分
    int Score = 0;
    char opponent = (player == 1) ? -1 : 1; //对手下的棋子
    //循环检查每个单元格
    for (row = 0; row < 8; row++)
    {
        for (col = 0; col < 8; col++)
        {
            //若该位置不可下子
            if (!isDown[row][col])
            {
                continue;   //继续
            }
            //复制棋盘各单元格下子的状态到临时数组
            for (i = 0; i < 8; i++)
            {
                for (j = 0; j < 8; j++)
                {
                    chessboard1[i][j] = chessboard[i][j];
                }
            }
            //在临时数组中的指定行列下子
            PlayStep(chessboard1, row, col, player);
            //获取下子后可得到的分数
            Score = GetMaxScore(chessboard1, player);
            //若原方案得到的分数小于本次下子的分数
            if (MaxScore < Score)
            {
                MaxScore = Score;   //保存最高分
            }
        }
    }
    return MaxScore;//返回得到的最高分
}
//AI自动下子
void AutoPlayStep(char chessboard[][8], int isDown[][8], char player)
{
    int row, col, row1, col1, i, j;
    //对方可下子提到的分数和最小分数
    int Score = 0, MinScore = 100;
    //临时数组,保存棋盘下子位置
    char chessboard1[8][8];
    //临时数组,保存可下子位置
    int isDown1[8][8];
    char opponent = (player == 1) ? -1 : 1; //对手下的棋子
    for (row = 0; row < 8; row++)   //循环检查棋盘每个单元格
    {
        for (col = 0; col < 8; col++)
        {
            //若不可下子
            if (isDown[row][col] == 0)
            {
                continue;//继续下一个位置
            }
            //将棋盘原来的棋子复制到临时数组中
            for (i = 0; i < 8; i++)
            {
                for (j = 0; j < 8; j++)
                {
                    chessboard1[i][j] = chessboard[i][j];
                }
            }
            //试着在临时棋盘中的一个位子下子
            PlayStep(chessboard1, row, col, player);
            //检查对手是否有地方可下子
            Check(chessboard1, isDown1, opponent);
            //获得临时棋盘中对方下子的得分情况
            Score = BestPlay(chessboard1, isDown1, opponent);
            //保存对方得分最低的下法
            if (Score < MinScore)
            {
                MinScore = Score;
                row1 = row;
                col1 = col;
            }
        }
    }
    //AI按最优下法下子
    PlayStep(chessboard, row1, col1, player);
}
int main()
{
    //保存棋盘中各单元格下子的状态
    char chessboard[8][8];
    //保存棋盘中各位置是否可以下子,可下子的位置为1,其余位置为0
    int isDown[8][8] = { 0 };
    int row, col, x, y;
    //已下棋子数量
    int iCount = 0;
    int player = 0; //下棋方
    //跳过下子的次数,若为2,表示双方都不能下子
    int SkipPlay = 0;
    //保存AI和游戏者的得分
    int Score[2];
    char select;
    printf("黑白棋\n\n");
    printf("游戏者执黑先下,AI执白,按回车键开始:\n");
    scanf("%c", &select);
    do
    {
        //计算下棋方(0表示游戏者,1表示AI)
        if (player == 0)
        {
            player = 1;
        }
        else
        {
            player = 0;
        }
        iCount = 4; //累计下子数
        //棋盘各位置清空
        for (row = 0; row < 8; row++)
        {
            for (col = 0; col < 8; col++)
            {
                chessboard[row][col] = 0;
            }
        }
        //在棋盘中间位置放置白棋
        chessboard[3][3] = chessboard[4][4] = 1;
        //在棋盘中间位置放置黑棋
        chessboard[3][4] = chessboard[4][3] = -1;
        printf("\n棋盘初始状态:\n");
        //显示初始棋盘下子的状况
        Output(chessboard);
        do
        {
            //若是游戏者下棋(下白子)
            if (player == 1)
            {
                player = 0;
                //判断是否可下黑子
                if (Check(chessboard, isDown, 2))
                {
                    //死循环,直到用户输入正确的坐标为止
                    while(1)
                    {
                        fflush(stdin);
                        printf("输入下子的位置(行 列):");
                        scanf("%d%c", &x, &y);
                        x--;    //计算行坐标位置
                        if(y >= ‘a‘)
                        {
                            y = y - ‘a‘ + 1;
                        }
                        else
                        {
                            y = y - ‘A‘ + 1;
                        }
                        y--;    //计算列位置
                        //若行列坐标输入有效
                        if (x >= 0 && y >= 0 && x < 8 && y < 8 && isDown[x][y])
                        {
                            //在指定坐标位置下黑子
                            PlayStep(chessboard, x, y, 2);
                            iCount++;   //累加下子数
                            break;
                        }
                        else
                        {
                            printf("坐标输入错误,请重新输入。\n");
                        }
                    }
                    printf("\n你下子后的状态:\n");
                    Output(chessboard); //显示棋子状态
                    printf("按任意键AI下子。\n");
                    getch();
                }
                //若无效下子的次数小于2
                else if (++SkipPlay < 2)
                {
                    fflush(stdin);  //清除输入缓冲区
                    printf("你没位置可下,按回车键让对方下子。");
                    scanf("%c", &select);
                } else
                {
                    printf("双方都没地方下子,游戏结束!\n");
                }
            }
            //若是AI下棋(下黑子)
            else
            {
                player = 1;
                //检查是否可下白子
                if (Check(chessboard, isDown, 1))
                {
                    SkipPlay = 0;   //清除无效下子次数
                    //AI下一个白子
                    AutoPlayStep(chessboard, isDown, 1);
                    iCount++;   //累加下子数
                    printf("\nAI下子后的状态:\n");
                    Output(chessboard); //显示棋子状态
                }
                else
                {
                    //若无效下子次数小于2
                    if (++SkipPlay < 2)
                    {
                        printf("我没位置可走,请你走。\n");
                    }
                    else
                    {
                        printf("双方都没地方下子,游戏结束!");
                    }
                }
            }
        }
        //下子数量小于64 且无效下子的次数小于2
        while (iCount < 64 && SkipPlay < 2);
        //显示各双方棋子的状况
        Output(chessboard);
        Score[0] = Score[1] = 0;//清空计分变量
        //循环统计各单元格黑白棋子的数量
        for (row = 0; row < 8; row++)
        {
            for (col = 0; col < 8; col++)
            {
                //统计黑子数
                Score[0] += chessboard[row][col] == -1;
                //统计白子数
                Score[1] += chessboard[row][col] == 1;
            }
        }
        printf("最终成绩:\n");
        printf("AI:%d\n游戏者:%d\n", Score[0], Score[1]);
        fflush(stdin);  //清空输入缓冲区
        printf("继续下一局(y/n)?:");
        scanf("%c", &select);
    }while (select == ‘y‘ || select == ‘Y‘);
    printf("Game Over!\n");
    return 0;
}

运行演示:

参考资源:

《零基础学算法》 第三版 戴艳等编 机械工业出版社



代码下载地址

http://download.csdn.net/download/u013553804/9500500

http://pan.baidu.com/s/1o8nbtFG



欢迎关注我的微信个人订阅号

每天多学一点0.0

时间: 2024-10-13 21:42:32

人机对战-黑白棋的相关文章

C语言-黑白棋(人机对战)

1 #include <stdio.h> 2 #include <conio.h> 3 /* 4 分析:要实现黑白棋的关键在于由计算机下棋时,智能判断最佳下棋位置.具体思路如下: 5 1.定义一个二维数组chessboard[8][8],用于保存黑白双方所下棋子的位置.如果数组元素为0,表示该单元格为落子:如果是-1,表示该单元格是黑子:如果是1,则表示该单元格是白子. 6 2.当一方下棋时,先检查是否有位置可下,如果没有就让对手下,如果有就让玩家或计算机下. 7 3. 若玩家下,

C语言 AI智能,五子棋 人机对战,人人对战

C语言五子棋,人机对战,人人对战 [email protected]:~$ cat main.c  # include<stdio.h> # include<string.h> # include<stdlib.h> # define SPA 0 # define MAN 1 # define COM 2    /* 空位置设为0 ,玩家下的位置设为1 ,电脑下的位置设为2 */ int qipan[15][15];    /* 15*15的棋盘 */ int a,b,

Android 蓝牙对战五子棋项目实现(含人机对战功能)

上周花了一周时间做的课程设计的项目,实现的功能如下: 基本功能: (1) 该APP能够通过蓝牙自动搜索周围其他使用了该APP的手机,用户可选择其中某一个APP发起对战的要求,被发起方可以同意或者拒绝: (2) 双方可以实现五子棋蓝牙对战: (3) 具备悔棋等功能. (4) 实现人机对战.提供难度选择. (5)提供用户战绩排名系统. 项目已经上传到Github:https://github.com/jiangzhengnan/PumpkinGoBang.git Github跳转 下面是界面截图和实

介绍一款Android小游戏--交互式人机对战五子棋

文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6589025 学习Android系统开发之余,编写了一个小游戏--交互式人机对战五子棋,自娱自乐.之所以称之为交互式人机对战五子棋,一是因为在进入人机对战模式这前,你可以任意设置好开局,同时,在对战过程中,你可以看到机器的思考过程,还可以标识出每一个落子点的优劣势:二是因为可以为机器增加游戏经验,使得机器越来越聪明.希望喜欢五子棋的同学能够喜欢,

游戏开发(三)——WIN32 黑白棋(三)——游戏画面的现实

整个游戏分3部分介绍. 1.棋局的现实 2.AI的现实 3.游戏画面的现实 提供一下完整项目下载 这是第三部分:画面的显示 这部分其实就比较简单的,说白了就是api的堆砌. 主要了解下windows的消息机制,以及怎么画图 主要是分别封装了下对棋盘,棋子,以及当前轮到谁,当前比分是多少,就是游戏画面上不同的部分的绘制. void DrawReversiBoard(); void DrawReversiPieces(EnumReversiPiecesType type, int row_y, in

HTML5+JS 《五子飞》游戏实现(八)人机对战

要想实现人机对战,就必须让电脑自动下棋,而且要知道自动去查找对方的棋子,看看有没有可以挑一对的,有没有可以夹一个的,这样下起来才有意思. 当电脑用户下完棋后,电脑应立即搜索用户的棋子,然后如果没有被吃的,就再随机走一个棋子(要想让电脑成为下棋高手就不能随机下棋,要考虑下棋后会不会再被对方吃掉等等,这里只简单做随机下子了). 完整<五子飞>游戏人机对战:http://www.lyout.com/projects/Fiveflychess/FiveflyChess8.htm // 查找是否有可挑的

黑白棋游戏 (codevs 2743)题解

[问题描述] 黑白棋游戏的棋盘由4×4方格阵列构成.棋盘的每一方格中放有1枚棋子,共有8枚白棋子和8枚黑棋子.这16枚棋子的每一种放置方案都构成一个游戏状态.在棋盘上拥有1条公共边的2个方格称为相邻方格.一个方格最多可有4个相邻方格.在玩黑白棋游戏时,每一步可将任何2个相邻方格中棋子互换位置.对于给定的初始游戏状态和目标游戏状态,编程计算从初始游戏状态变化到目标游戏状态的最短着棋序列. [样例输入] 1111 0000 1110 0010 1010 0101 1010 0101 [样例输出] 4

枚举(黑白棋)

/*代码一:DFS+Enum*/ //Memory Time //240K 344MS //本题只要求输出翻转的次数,因此BFS或DFS都适用 #include<iostream> using namespace std; bool chess[6][6]={false};//利用的只有中心的4x4 bool flag; int step; int r[]={-1,1,0,0,0};//便于翻棋操作 int c[]={0,0,-1,1,0}; bool judge_all(void)//判断&

高考作文,人机对战,人工智能只有小学水平

3月份时的人机围棋大战,国际顶尖选手李世石遭阿尔法狗完爆,这次人机对战考验的是围棋的逻辑能力,而计算机本就是逻辑产物,李世石落败情理之中.在昨天高考作文的人机大战中,拼的不仅是逻辑,更重要的语言文字的情感表达能力.在此次直接的语言文字比拼中,人工智能的情感表述尚显稚嫩. 人工智能情感表述尚显稚嫩,高考作文水平不及格 语文高考作文每年都是网络热点话题之一,而在昨天微软小冰也参加了高考作文创作,这被很多网友认为是人工智能与人类又一场PK,微软小冰同场与张鸣.安意如.潘采夫等10位名家进行高考作文创作