c语言实现五子棋代码(有简单的人机对战,附解析)


前段时间,我和我的小伙伴一起做了关于五子棋的小的团队项目,我将其中一些非核心的内容简化了一下,保留了核心的内容,今天我就和你们来分享一下我们所做小项目。

因为我们定义了比较多的函数,所以我将其每个函数都单独讲解,在这个项目中,我做了两个人机对战,一个非常简单的,一个简单的。

(声明函数就不展示了)
int flag = 0;
int map[19][19];
我们先定义两个全局变量。
其中flag变量用来表示这个回合谁落子,偶数表示黑方落子,基数表示白方落子。
数组中的数组代表子的颜色,2表示黑子,1表示白字。
system(“cls”); 用来清屏。

主函数

int main() {

menuView();
return 0;

}
这里主要是调用菜单界面函数。

游戏界面函数

void gameView(void) {

int n, q, w, e = 0, r = 0, t, y, b, m, i = 0;
while (1) {
    init();
    gameView_ShowMap();
    while (i == 0) {
    
        if (flag % 2 == 0 && e != 2) {
            printf("请输入黑子坐标(输入20 20)暂停");
            scanf("%d %d", &q, &w);
            if (q + w >= 40)
                zantingView();
            t = playerMove(q, w);
            if (t == 2) {
            
                printf("落子成功\n");
            }
            e = isWin(q, w);
            if (e == 2) {
                printf("黑子赢\n");
                printf("白子输\n");
                jieshuView();

            }
        }
        if (flag % 2 == 1 && r != 1) {
            printf("请输入白子坐标(输入20 20)暂停");
            scanf("%d %d", &q, &w);
            if (q + w >= 40)
                zantingView();
            y = playerMove(q, w);
            if (y == 1) {
                printf("落子成功\n");
            }
            r = isWin(q, w);
            if (r == 1) {
                printf("白子赢\n");
                printf("黑子输\n");
                jieshuView();
            }
        }
    };
}
return;

}
在这里我们先初始化棋盘(也就是void init(void)),之后打印棋盘。
然后我们就可以输入想要落子的坐标了,在这里如果我们输入20 20那么就会进入暂停界面,当我们输入了落子坐标后,会将我们输入的坐标作为参数传入到落子的函数当中,如果成功落子,那么就会返回一个y=1的值,之后我们通过检测y的值就可以判断我们是否落子成功了。如果成功了,那么我们就可以调用胜利判断的函数,进行胜利的判断。判断胜利函数会返回一个值,我们可以通过检测函数返回的值来判断是否胜利,如果胜利了,那么我们就会打印哪一方赢了,哪一方输入,并且调用结算节目函数。

菜单界面函数

void menuView(void) {

while (1) {
    printf("1. 面对面模式\n");
    printf("2. 人机对战\n");
    printf("3. 进入设置\n");
    printf("4. 退出游戏\n");
    printf("请输入序号:");
    int choose;
    scanf("%d", &choose);
    switch (choose) {
    case 1:
        system("cls");
        gameView();
        break;
    case 2:
        system("cls");
        nanduView();
    case 3:
        printf("敬请期待\n");
        break;
    case 4:
        exit(0);
        break;
    default:
        break;
    }
}

}
进入了菜单函数,我们就可以进行选择,在这里 system(“cls”); 是用来进行清屏的,避免许多界面堆在一起。

暂停键界面函数

void zantingView(){

int x = 0,y;
printf("1.结束对局\n");
printf("2.返回游戏\n");
printf("请输入编号:\n");
scanf("%d", &x);

if (x != 1 && x != 2)
{
    printf("输入错误,请重新输入");
    scanf("%d", &x);
}

switch (x) {
case 1:
    if (flag % 2 == 1) {
        system("cls");
        printf("黑子赢\n");
        printf("白子输\n");
        jieshuView();
        
    }
    if (flag % 2 == 0) {
        system("cls");
        printf("白子赢\n");
        printf("黑子输\n");
        jieshuView();
    }
    break;
case 2:
    return;
default:
    break;
}
return;

}
暂停界面我们可以进行选择,如果选择结束游戏,那么就直接调用结算界面函数。

最简单人机对战函数

void jiandanView() {

int q, w, e = 0, t, r = 0;
while (1) {
    init();
    gameView_ShowMap();
    while (1) {
        if (flag % 2 == 0 && e != 2) {
            printf("请输入黑子坐标(输入20 20)暂停");
            scanf("%d %d", &q, &w);
            if (q + w >= 40)
                zantingView();
            t = playerMove(q, w);
            if (t == 2) {
                printf("落子成功\n");
            }
            e = isWin(q, w);
            if (e == 2) {
                printf("黑子赢\n");
                printf("白子输\n");
                jieshuView();

            }
        }
        if (flag % 2 == 1 && r != 1) {
            q = rand() % 20;
            w = rand() % 20;
            playerMove(q, w);
            r = isWin(q, w);
            if (r == 1) {
                printf("白子赢\n");
                printf("黑子输\n");
                jieshuView();
            }
        }
    }
}

}
玩家部分和前文的游戏界面函数相同,我这里主要讲解一下人机如何落子。
在这个函数中,我们采用了随机函数来生成随机数用来进行人机落子。
我们生成了两个随机数,代表两个坐标。

简单人机对战函数

void renjiView() {

int q, w, e = 0, t, r = 0, p = 0, k = 0;
while (1) {
    init();
    gameView_ShowMap();
    while (1) {
        if (flag % 2 == 0 && e != 2) {
            printf("请输入黑子坐标(输入20 20)暂停");
            scanf("%d %d", &q, &w);
            if (q + w >= 40)
                zantingView();
            t = playerMove(q, w);
            if (t == 2) {
                printf("落子成功\n");
            }
            e = isWin(q, w);
            if (e == 2) {
                printf("黑子赢\n");
                printf("白子输\n");
                jieshuView();
            }
        }
        if (flag % 2 == 1 && r != 1) {
            p = rand() % 8;
            switch (p) {
            case 1:
                if (map[q - 1][w - 1] == 0 && q - 1 > 0 && q - 1 < 18 && w - 1 > 0 && w - 1 < 18) {
                    map[q - 1][w - 1] = 1;
                    gameView_ShowMap();
                    k = isWin(q - 1, w - 1);
                    if (k == 1) {
                        printf("白子赢\n");
                        printf("黑子输\n");
                        jieshuView();
                    }
                    ++flag;
                    break;
                }
                else
                    p = rand() % 8;
                continue;
            case 2:
                if (map[q][w - 1] == 0 && q > 0 && q < 18 && w - 1 > 0 && w - 1 < 18) {
                    map[q][w - 1] = 1;
                    gameView_ShowMap();
                    k = isWin(q, w - 1);
                    if (k == 1) {
                        printf("白子赢\n");
                        printf("黑子输\n");
                        jieshuView();
                    }
                    ++flag;
                    break;

                }
                else
                    p = rand() % 8;
                continue;
            case 3:
                if (map[q + 1][w - 1] == 0 && q + 1 > 0 && q + 1 < 18 && w - 1 > 0 && w - 1 < 18) {
                    map[q + 1][w - 1] = 1;
                    gameView_ShowMap();
                    k = isWin(q + 1, w - 1);
                    if (k == 1) {
                        printf("白子赢\n");
                        printf("黑子输\n");
                        jieshuView();
                    }
                    ++flag;
                    break;
                }
                else
                    p = rand() % 8;
                continue;
            case 4:
                if (map[q + 1][w] == 0 && q + 1 > 0 && q + 1 < 18 && w > 0 && w < 18) {
                    map[q + 1][w] = 1;
                    gameView_ShowMap();
                    k = isWin(q + 1, w);
                    if (k == 1) {
                        printf("白子赢\n");
                        printf("黑子输\n");
                        jieshuView();
                    }
                    ++flag;
                    break;
                }
                else
                    p = rand() % 8;
                continue;
            case 5:
                if (map[q + 1][w + 1] == 0 && q + 1 > 0 && q + 1 < 18 && w + 1 > 0 && w + 1 < 18) {
                    map[q + 1][w + 1] = 1;
                    gameView_ShowMap();
                    k = isWin(q + 1, w + 1);
                    if (k == 1) {
                        printf("白子赢\n");
                        printf("黑子输\n");
                        jieshuView();
                    }
                    ++flag;
                    break;
                }
                else
                    p = rand() % 8;
                continue;
            case 6:
                if (map[q][w + 1] == 0 && q > 0 && q < 18 && w + 1 > 0 && w + 1 < 18) {
                    map[q][w + 1] = 1;
                    gameView_ShowMap();
                    k = isWin(q, w + 1);
                    if (k == 1) {
                        printf("白子赢\n");
                        printf("黑子输\n");
                        jieshuView();
                    }
                    ++flag;
                    break;
                }
                else
                    p = rand() % 8;
                continue;
            case 7:
                if (map[q - 1][w + 1] == 0 && q - 1 > 0 && q - 1 < 18 && w + 1 > 0 && w + 1 < 18) {
                    map[q - 1][w + 1] = 1;
                    gameView_ShowMap();
                    k = isWin(q - 1, w + 1);
                    if (k == 1) {
                        printf("白子赢\n");
                        printf("黑子输\n");
                        jieshuView();
                    }
                    ++flag;
                    break;
                }
                else
                    p = rand() % 8;
                continue;
            case 8:
                if (map[q - 1][w] == 0 && q - 1 > 0 && q - 1 < 18 && w > 0 && w < 18) {
                    map[q - 1][w] = 1;
                    gameView_ShowMap();
                    k = isWin(q - 1, w);
                    if (k == 1) {
                        printf("白子赢\n");
                        printf("黑子输\n");
                        jieshuView();
                    }
                    ++flag;
                    break;
                }
                else
                    p = rand() % 8;
                continue;
            default:
                break;
            }

        }
    }
}

}
这里我们的主要思路是,在玩家落子的四周随机落子来达到类似围堵的效果,if里许多的判断条件为了避免数组越界,并且在人机落子之后,我们调用判断胜利函数来判断人机是否胜利,最后用随机函数重新生成一个数。

落子函数

int playerMove(int x, int y) {

extern int flag;
extern int map[19][19];
if (map[x][y] == 0&&(x+y)<40&&x>0&&y>0) {
    if (flag % 2 == 0) {
        map[x][y] = 2;
        flag++;
        gameView_ShowMap();
        return 2;
    }
    else if (flag % 2 == 1) {
        map[x][y] = 1;
        flag++;
        gameView_ShowMap();
        return 1;
    }
}

}
在这里,我们先判断玩家输入的坐标是否符合我们的要求,如果符合了,那么就改变数组内对应的值,并且返回一个值用以判断是否落子成功。

判断胜利函数

int isWin(int x, int y) {

int b = 0, k, c, d,z;
extern int map[19][19];
if (map[x][y] == 2) {
    b += 1;
    for (k = 1; map[x - k][y - k] == 2; k++)
        b += 1;
    for (k = 1; map[x + k][y + k] == 2; k++)
        b += 1;
    for (k = 1, z = 1; map[x - k][y + k] == 2; k++)
        z += 1;
    for (k = 1; map[x + k][y - k] == 2; k++)
        z += 1;
    for (k = 1, c = 1; map[x + k][y] == 2; k++)
        c += 1;
    for (k = 1; map[x - k][y] == 2; k++)
        c += 1;
    for (k = 1, d = 1; map[x][y + k] == 2; k++)
        d += 1;
    for (k = 1; map[x][y - k] == 2; k++)
        d += 1;
    if (b >= 5 || c >= 5 || d >= 5 || z >= 5)
        return 2;

}
int e = 0, h, f, g,n;
if (map[x][y] == 1) {
    e += 1;
    for (h = 1; map[x - h][y - h] == 1; h++)
        e += 1;
    for (h = 1; map[x + h][y + h] == 1; h++)
        e += 1;
    for (k = 1, n = 1; map[x - k][y + k] == 2; k++)
        n += 1;
    for (k = 1; map[x + k][y - k] == 2; k++)
        n += 1;
    for (h = 1, f = 1; map[x + h][y] == 1; h++)
        f += 1;
    for (h = 1; map[x - h][y] == 1; h++)
        f += 1;
    for (h = 1, g = 1; map[x][y + h] == 1; h++)
        g += 1;
    for (h = 1; map[x][y - h] == 1; h++)
        g += 1;
    if (e >= 5 || f >= 5 || g >= 5 || n>=5)
        return 1;


}

}
这个判断胜利的函数看似复杂,其实并不怎么复杂,我们将玩家输入的坐标作为参数,开始逐个查找,先是左上方,右下方,之后是右上方,右下方,之后是正下方和正上方,之后是正右方,正左方逐个查找,最后判断是否满足胜利的条件,如果满足,就返回一个值到游戏界面函数,之后游戏界面函数通过判断这个值进入游戏结算函数

初始化棋盘函数

void init(void) {

extern int flag;
extern int map[19][19];
for (int i = 0; i < 19; i++)
{
    for (int j = 0; j < 19; j++)
    {
        map[i][j] = 0;
    }
}
flag = 0;
return;

}
这个函数通过两个循环将map数组初始化以便进行游戏。

打印棋盘函数

void gameView_ShowMap(void) {

extern int map[19][19];
int b = 0;
printf("            口为黑棋,龘为白棋,十为空地\n");
printf("   0  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15 16 17 18\n");
for (int i = 0; i < 19; i++) {
    printf("%-2d ", b++);
    for (int j = 0; j < 19; j++) {
        if (map[i][j] == 0) {
            printf("十 ");
        }
        else if (map[i][j] == 1) {
            printf("龘 ");
        }
        else {
            printf("口 ");
        }
    }
    printf("\n");
}
return;

}
这个函数用来打印棋盘,如果是空白就为十,黑子就为口,白字就为龘。

结算界面函数

void jieshuView() {

int y;
printf("请输入编号:\n");
printf(" 1.退出游戏\n");
printf(" 2.返回菜单\n");
scanf_s("%d", &y);
if (y != 1 && y != 2) {
    printf("输入错误,请重新输入:");
    scanf_s("%d", &y);
}
switch (y) {
case 1:
    exit(0);
    break;
case 2:
    system("cls");
    menuView();
    break;
default:
    break;
}

}
结算界面函数让我们进行选择,我们可以选择返回菜单继续游戏,也可以直接退出游戏

暂停界面

void zantingView() {

int x = 0,y;
printf("1.结束对局\n");
printf("2.返回游戏\n");
printf("请输入编号:\n");
scanf("%d", &x);

if (x != 1 && x != 2)
{
    printf("输入错误,请重新输入");
    scanf("%d", &x);
}

switch (x) {
case 1:
    if (flag % 2 == 1) {
        system("cls");
        printf("黑子赢\n");
        printf("白子输\n");
        jieshuView();
        
    }
    if (flag % 2 == 0) {
        system("cls");
        printf("白子赢\n");
        printf("黑子输\n");
        jieshuView();
    }
    break;
case 2:
    return;
default:
    break;
}
return;

}
暂停界面我们有两个选择,一个是返回游戏,一个是结束游戏,如果结束游戏那么就直接进入游戏结算函数,如果返回游戏,则游戏继续。

至此,代码讲解完毕。
(本人是新手写作,可能有很多地方没有解释清楚并且可能有一些出错的地方,请大家谅解,由于自己技术不足,代码可读性可能很差,在这里我说一声抱歉,也请大家谅解。并且代码可能有许多可以简化的地方,但是我还没发现,也请各位指出。

用c#编写的五子棋人机 核心算法 核心算法就是计算计算机应该在哪里落子。    思路的伪代码如下。    PC_Stone    For i = 1 to 15     For j = 1 to 15     If ( board[i][j] != -1)     Qz[i][j] = -     Esle     FindQz(Qz[i][j])    getTheMaxQz()    而在这个过程中最主要的算法是计算每个点的权重,由此判断电脑应该将棋子落在哪个地方。    计算确定点的权重的函数是FindQz();,函数里面有对于多种不同情况下,函数所赋给那个点的权重值,这些值是累加的。函数主要通过对四个函数X1(),X2(),X3(),X4()的调用来确定每个点所处地位。    FindQz()函数可以分为两部分。    第一部分是假设人在此点落下一子后,此点给人所带来的好处是多少。通过对函数X1()计算如果在点board[i][j]落下,所在行有多少连续的相同的点数。与X1(),类似的是X2()是计算board[i][j]所在列的点数。X3()是计算左高右低的斜排,X4()是左低右高的情况。经过计算后,将这四种情况所带来的改变加到一起,就是将棋子落在这里对假设方带来的好处。    第二部分是假设电脑在此落一点之后,此点给电脑带来的好处是多少。调用过程与第一部分基本一样,没什么不同。    经过这样的调用,将两部分计算出的结果加到一起,算出来的就是电脑下在这一点会带来的所有影响。选取影响最大的一点,落子,这样就能在一定程度上达到某种智能。    对于X1()函数,他的作用实现是这样的。运用两个计数器count与flag,count用于计算有多少相连的相同的子,并且是一board[i][j]为中心,向两边发散(说他是中心并不意味着他是相连的点的中心位置)。Flag是计算相连的子的两端是否有阻挡。阻挡分为两部分,一部分是到了表格的尽头,另一种是被另一种颜色的棋子挡住。这样综合count与flag两个参数,给出board[i][j]点对于行的贡献值。    对于X2(),X3(),X4()他们的原理是与X1()一样的,只不过是坐标不同罢了。这样,计算完之后,再进行比较,就能得到最好的点了。        对于特殊落点的判断问题:   设以围棋棋盘左下角为坐标原点建立直角坐标系xOy,   若(9,10)(9,11)(10,10)(11,9)上有黑子,(8,12)(10,9)(11,8)上有白子,现在到白棋走子,   若走(10,12)(11,11)就属于斜向走子,但是通常直向的(紧贴着棋子走的)走子要比斜向的走子对防守的贡献大,   若走(8,11)(8,10)(8,9)(9,9)其中一个的均属于单侧走子,而另一侧则空虚,所以也不好,   若走(11,10)也不好,因为在(11,8)的子已经对防守有了一定的贡献了,同理走(10,11)的也不好,因为(10,9)的子也对防守有了一定的贡献.所以说此时最佳的走子方法就是走(9,12)   若(8,10)有白子,(10,10)(11,10)(12,10)有黑子,到白子走,由于(8,10)对防守贡献了一部分,所以应该走(13,10)而不走(9,10)。   若为黑走,就应该走(13,10)而不走(9,10),因为白子的(8,10)会削弱它对攻的贡献
### 五子棋AI对C语言实现源码示例 #### 函数定义与初始化 为了构建一个能够支持人机博弈的五子棋游戏,在C语言环境中,通常会先定义全局变量来表示棋盘状态以及玩家信息。下面展示的是部分用于初始化和管理游戏过程的关键函数。 ```c #include <stdio.h> #define SIZE 15 // 定义标准大小为15*15的棋盘 char board[SIZE][SIZE]; // 创建二维数组作为棋盘 int player; // 当前玩家标记(0=黑,1=白) void initBoard() { for(int i = 0 ; i<SIZE;i++) { for(int j = 0;j<SIZE;j++){ board[i][j]='+';// 使用'+'代表空白位置 } } } ``` 这段代码设置了棋盘尺寸并创建了一个名为`board`的二维字符数组用来存储当前的游戏局面;同时还有一个整型变量`player`用于跟踪哪位选手正在下棋[^2]。 #### 用户交互逻辑 为了让人类玩家可以参与到游戏中去,还需要编写接收用户输入坐标的接口,并验证这些坐标是否合法有效: ```c // 获取用户的落子位置 void getUserMove(){ int row,col; printf("请输入你要下的行列号(如:7 8),以空格分隔:"); scanf("%d %d",&row,&col); while(row<0 || col<0 || row>=SIZE || col >= SIZE || board[row][col]!='+') { printf("非法的位置,请重新输入:\n"); scanf("%d %d", &row, &col); } placeStone(row, col, 'B'); // 假设'B'代表黑色棋子 } // 放置棋子到指定位置 void placeStone(int r,int c,char stone){ if(board[r][c]=='+'){ board[r][c]=stone; }else{ printf("该处已有棋子\n"); } } ``` 这里实现了两个重要功能——获取用户命令行形式给出的目标格子坐标(`getUserMove`) 和 将选定颜色('B') 的棋子放置于特定单元格内 (`placeStone`). 同时也加入了简单的边界条件检查机制确保不会越界访问内存地址或覆盖已存在的棋子[^4]. #### AI决策模块 针对简单版本的人工智能对手,则可以通过随机选取未被占用的空间来进行模拟对抗: ```c // 随机生成电脑走法 void aiMove(){ srand(time(NULL)); int row=-1,col=-1; do{ row=rand()%SIZE; col=rand()%SIZE; }while(board[row][col]!='+'); placeStone(row, col,'W');//假设"W"代表白色棋子 } ``` 上述方法利用了伪随机数发生器来决定机器人的下一步行动方向,直到找到合适的空闲节点为止。这种方法虽然不够聪明但也足以构成初级难度级别的挑对象[^1]. #### 游戏循环控制 最后一步就是把所有的组件组合起来形成完整的回合制玩法框架: ```c int main(){ char winner='N'; initBoard(); while(winner=='N'){ printBoard(); // 显示最新状况 getUserMove(); // 轮到真人玩家出手 checkWinCondition(&winner); if(winner!='N') break; aiMove(); // 接着轮到计算机应答 checkWinCondition(&winner); if(isFull()){ winner='D'; // 平局情况处理 } } announceResult(winner); // 输出最终结果 return 0; } ``` 以上即为一个简化版的支持单机模式运行且具备基础人工智能特性的五子棋应用程序概貌描述.
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值