目录
C语言实现三子棋(会堵棋,加强版)智能AI博弈
三子棋介绍:
三子棋是一种民间传统游戏,又叫九宫棋、圈圈叉叉棋、一条龙、井字棋等。游戏分为双方对战,双方依次在9宫格棋盘上摆放棋子,率先将自己的三个棋子走成一条线就视为胜利,而对方就算输了,但是三子棋在很多时候会出现和棋的局面。
功能实现:
1:打印菜单
2:棋盘初始化
3:打印棋盘
4:玩家下棋
5:电脑下棋(优化)
6:判断输赢
1:打印菜单
void meue() {
printf("**************************\n");
printf("**************************\n");
printf("****1.Play******0.Exit****\n");
printf("**************************\n");
printf("**************************\n");
}
2:棋盘初始化
首先,三子棋是一个3*3的棋盘,所以我们可以用一个二维数组来表示这个棋盘,当没有下棋的时候棋盘为空,而怎么用二维数组表示呢?
我们可以将一个char类型的二维数组全部初始化为空格,这样就能表示空棋盘了
3:打印棋盘
首先对棋盘进行分析:
一共5行,第一行由空格(' ')和 | 组成,但 | 只有两个,因此需要进行判断
第二行全部由---组成,却只有两行,所以也需要进行判断
这是一个3*3的棋盘,row=3,col=3。条件则为i<row-1的时候打印 | ,j<col-1的时候打印---
void print_board(char board[ROW][COL], int row, int col) {
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
printf(" %c ", board[i][j]);
if (j < col - 1)
{
printf("|");
}
}
printf("\n");
if (i < row - 1)
{
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
{
printf("|");
}
}
printf("\n");
}
}
}
4:玩家下棋
玩家下棋的时候X和Y的坐标范围都是(1,3),但数组是从0开始算的,所以我们需要将X和Y减1才能作为数组中的下标使用,在下棋子的时候需要进行判断,这个坐标是否合法,是否没有被占用
void player_move(char board[ROW][COL], int row, int col) {
printf("玩家下棋\n");
while (1) {
printf("请输入要下的坐标:>");
int x = 0, y = 0;
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= y && y <= col) {
if (board[x - 1][y - 1] == ' ') {
board[x - 1][y - 1] = '*';
break;
}
else {
printf("坐标已经被占用,请重新输入\n");
}
}
else {
printf("坐标不合法,请重新输入\n");
}
}
}
5:电脑下棋
首先展示无脑版本,堵棋全看运气->利用随机数种子来下棋,随机生成的数组%3得到的范围就是(0,2),刚好是数组的下标范围
void cmp_move(char board[ROW][COL], int row, int col) {
printf("电脑下棋:\n");
while (1) {
int x = rand() % row;
int y = rand() % col;
if (board[x][y] == ' ') {
board[x][y] = '#';
break;
}
}
}
接下来是会堵棋的版本:
思路很简单,直接遍历整个数组,行 列 对角线是否有满足凑齐了两个还空一个的位置,是的话就堵,这里可以使用一个计数器来记录该方向是否存在两个相同的棋子,并且是否有空的位置,我们新定义两个变量X和Y来记录空格的位置,将X和Y初始化为-1,这样就可以判断出是否真的找到了空位,而不需要用flag来判断
而什么时候进攻,什么时候防守呢?那当然是能进攻就进攻,不能进攻则防守咯。
还有就是能抢(2,2)的位置一定要抢,抢不到则在四个角落随便找个位置,这里我选择左上角的位置
int intelligence_play(char board[ROW][COL], int row, int col) {
//先抢占(2,2)的位置,若已经被占用,则下四个角落之一,以左上角为例(1,1)->对应的数组坐标为(0,0);
if (board[1][1] == ' ') {
board[1][1] = '#';
return 1;
}
else if(board[0][0]==' ') {
board[0][0] = '#';
return 1;
}
//抢占位置后判断是攻还是防
//cnt用于记录玩家棋子个数,cnt1为电脑,x,y为空格的坐标
int cnt = 0, cnt1 = 0, x = -1, y = -1, x1 = -1, y1 = -1, flag = 1;
//先判断自己是否满足胜利条件
//判断行
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (board[i][j] == '#') {
cnt++;
}
if (board[i][j] == ' ') {
//记录空的位置
x = i, y = j;
}
if (cnt == 2&&x!=-1&&y!=-1) {
//开始进攻,连成三个棋,计数清零
cnt = 0;
board[x][y] = '#';
x = -1, y = -1;
return 1;
}
}
//每一行检查结束后需要计数清零
cnt = 0;
x = -1, y = -1;
}
//是否需要进行堵棋
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (board[i][j] == '*') {
cnt1++;
}
if (board[i][j] == ' ') {
x = i, y = j;
}
if (cnt1 == 2 && x != -1 && y != -1) {
cnt = 0;
board[x][y] = '#';
x = -1, y = -1;
return 1;
}
}
cnt1 = 0;
x = -1, y = -1;
}
//判断列
for (int j = 0; j < col; j++) {
for (int i = 0; i < row; i++) {
if (board[i][j] == '#') {
cnt++;
}
if (board[i][j] == ' ') {
x = i, y = j;
}
if (cnt == 2 && x != -1 && y != -1) {
cnt = 0;
board[x][y] = '#';
x = -1, y = -1;
return 1;
}
}
cnt = 0;
x = -1, y = -1;
}
for (int j = 0; j < col; j++) {
for (int i = 0; i < row; i++) {
if (board[i][j] == '*') {
cnt++;
}
if (board[i][j] == ' ') {
x = i, y = j;
}
if (cnt == 2 && x != -1 && y != -1) {
cnt = 0;
board[x][y] = '#';
x = -1, y = -1;
return 1;
}
}
cnt = 0;
x = -1, y = -1;
}
//判断逆对角线
for (int i = 0; i < row; i++) {
if (board[i][row - i - 1] == '#') {
cnt++;
}
if (board[i][row - i - 1] == ' ') {
x = i, y = row - i - 1;
}
if (cnt == 2 && x != -1 && y != -1) {
cnt = 0;
board[x][y] = '#';
x = -1, y = -1;
return 1;
}
}
cnt = 0, x = -1, y = -1;
for (int i = 0; i < row; i++) {
if (board[i][row - i - 1] == '*') {
cnt++;
}
if (board[i][row - i - 1] == ' ') {
x = i, y = row - i - 1;
}
if (cnt == 2 && x != -1 && y != -1) {
cnt = 0;
board[x][y] = '#';
x = -1, y = -1;
return 1;
}
}
cnt = 0, x = -1, y = -1;
//判断逆对角线
for (int i = 0; i < row; i++) {
if (board[i][i] == '#') {
cnt++;
}
if (board[i][i] == ' ') {
x = i, y = row - i - 1;
}
if (cnt == 2 && x != -1 && y != -1) {
cnt = 0;
board[x][y] = '#';
x = -1, y = -1;
return 1;
}
}
cnt = 0, x = -1, y = -1;
for (int i = 0; i < row; i++) {
if (board[i][i] == '*') {
cnt++;
}
if (board[i][i] == ' ') {
x = i, y = row - i - 1;
}
if (cnt == 2 && x != -1 && y != -1) {
cnt = 0;
board[x][y] = '#';
x = -1, y = -1;
return 1;
}
}
cnt = 0, x = -1, y = -1;
//如果上述条件都不满足,则随机下棋
cmp_move(board, row, col);
}
5:输赢的判断
先判断棋盘是否已经满了,这个很简单,直接遍历整个数组,如果还能找到有空格的位置就还没有满,找不到了就满了
int isfull(char board[ROW][COL], int row, int col) {
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (board[i][j] == ' ') {
return 0;
}
}
}
return 1;
}
接下来是判断输赢:
#为电脑赢,*为玩家赢了,Q为平局,C为继续游戏
用两个计数器来记录玩家和电脑棋子在该方向上的棋子数量,当等于3的时候就胜利
char iswin(char board[ROW][COL], int row, int col) {
int num0f0 = 0;//玩家
int num0fX = 0;//电脑
//判断行
for (int i = 0; i < row; i++) {
num0f0 = num0fX = 0;
for (int j = 0; j < col; j++) {
if (board[i][j] == '*') {
num0f0++;
}
else if (board[i][j] == '#') {
num0fX++;
}
}
if (num0f0 == ROW) {
return '*';
}
else if (num0fX == COL) {
return '#';
}
}
//判断列
for (int j = 0; j < col; j++) {
num0f0 = num0fX = 0;
for (int i = 0; i < row; i++) {
if (board[i][j] == '*') {
num0f0++;
}
else if (board[i][j] == '#') {
num0fX++;
}
}
if (num0f0 == row) {
return '*';
}
else if (num0fX == col) {
return '#';
}
}
//判断正对角线
num0f0 = num0fX = 0;
for (int i = 0; i < row; i++) {
if (board[i][i] == '*') {
num0f0++;
}
else if (board[i][i] == '#') {
num0fX++;
}
}
if (num0f0 == row) {
return '*';
}
else if (num0fX == col) {
return '#';
}
//检查逆对角线
num0f0 = num0fX = 0;
for (int i = 0; i < row; i++) {
if (board[i][row - i - 1] == '*') {
num0f0++;
}
else if (board[i][row - i - 1] == '#') {
num0fX++;
}
}
if (num0f0 == row) {
return '*';
}
else if (num0fX == col) {
return '#';
}
//判断是否平均,是否需要继续
int ret = isfull(board, ROW, COL);
if (ret == 1) {
return 'Q';
}
else {
return 'C';//继续游戏
}
}
代码总和:
game.h文件
#pragma once
#define COL 3
#define ROW 3
//函数申明
void init_board(char board[ROW][COL],int row, int col);
void print_board(char board[ROW][COL], int row, int col);
int isfull(char board[ROW][COL], int row, int col);
char iswin(char board[ROW][COL], int row, int col);
game.c文件
#include"game.h"
#include<stdlib.h>
#include<time.h>
//初始化棋盘为空格
void init_board(char board[ROW][COL], int row, int col) {
for (int i = 0; i < row;i++)
{
for (int j = 0; j < col;j++)
{
board[i][j] = ' ';
}
}
}
//打印棋盘
void print_board(char board[ROW][COL], int row, int col) {
int i = 0;
for (i = 0; i < row; i++)
{
int j = 0;
for (j = 0; j < col; j++)
{
printf(" %c ", board[i][j]);
if (j < col - 1)
{
printf("|");
}
}
printf("\n");
if (i < row - 1)
{
for (j = 0; j < col; j++)
{
printf("---");
if (j < col - 1)
{
printf("|");
}
}
printf("\n");
}
}
}
void player_move(char board[ROW][COL], int row, int col) {
printf("玩家下棋\n");
while (1) {
printf("请输入要下的坐标:>");
int x = 0, y = 0;
scanf("%d %d", &x, &y);
if (x >= 1 && x <= row && y >= y && y <= col) {
if (board[x - 1][y - 1] == ' ') {
board[x - 1][y - 1] = '*';
break;
}
else {
printf("坐标已经被占用,请重新输入\n");
}
}
else {
printf("坐标不合法,请重新输入\n");
}
}
}
//电脑下棋,简单版本->随机生成坐标rand()
void cmp_move(char board[ROW][COL], int row, int col) {
printf("电脑下棋:\n");
while (1) {
int x = rand() % row;
int y = rand() % col;
if (board[x][y] == ' ') {
board[x][y] = '#';
break;
}
}
}
//判断棋盘是否已经满了,如果满了则返回1,没满返回0
int isfull(char board[ROW][COL], int row, int col) {
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (board[i][j] == ' ') {
return 0;
}
}
}
return 1;
}
//判断胜负
//#为电脑赢,*为玩家赢了,Q为平局,C为继续游戏
char iswin(char board[ROW][COL], int row, int col) {
int num0f0 = 0;//玩家
int num0fX = 0;//电脑
//判断行
for (int i = 0; i < row; i++) {
num0f0 = num0fX = 0;
for (int j = 0; j < col; j++) {
if (board[i][j] == '*') {
num0f0++;
}
else if (board[i][j] == '#') {
num0fX++;
}
}
if (num0f0 == ROW) {
return '*';
}
else if (num0fX == COL) {
return '#';
}
}
//判断列
for (int j = 0; j < col; j++) {
num0f0 = num0fX = 0;
for (int i = 0; i < row; i++) {
if (board[i][j] == '*') {
num0f0++;
}
else if (board[i][j] == '#') {
num0fX++;
}
}
if (num0f0 == row) {
return '*';
}
else if (num0fX == col) {
return '#';
}
}
//判断正对角线
num0f0 = num0fX = 0;
for (int i = 0; i < row; i++) {
if (board[i][i] == '*') {
num0f0++;
}
else if (board[i][i] == '#') {
num0fX++;
}
}
if (num0f0 == row) {
return '*';
}
else if (num0fX == col) {
return '#';
}
//检查逆对角线
num0f0 = num0fX = 0;
for (int i = 0; i < row; i++) {
if (board[i][row - i - 1] == '*') {
num0f0++;
}
else if (board[i][row - i - 1] == '#') {
num0fX++;
}
}
if (num0f0 == row) {
return '*';
}
else if (num0fX == col) {
return '#';
}
//判断是否平均,是否需要继续
int ret = isfull(board, ROW, COL);
if (ret == 1) {
return 'Q';
}
else {
return 'C';//继续游戏
}
}
int intelligence_play(char board[ROW][COL], int row, int col) {
//先抢占(2,2)的位置,若已经被占用,则下四个角落之一,以左上角为例(1,1)->对应的数组坐标为(0,0);
if (board[1][1] == ' ') {
board[1][1] = '#';
return 1;
}
else if(board[0][0]==' ') {
board[0][0] = '#';
return 1;
}
//抢占位置后判断是攻还是防
//cnt用于记录玩家棋子个数,cnt1为电脑,x,y为空格的坐标
int cnt = 0, cnt1 = 0, x = -1, y = -1, x1 = -1, y1 = -1, flag = 1;
//先判断自己是否满足胜利条件
//判断行
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (board[i][j] == '#') {
cnt++;
}
if (board[i][j] == ' ') {
//记录空的位置
x = i, y = j;
}
if (cnt == 2&&x!=-1&&y!=-1) {
//开始进攻,连成三个棋,计数清零
cnt = 0;
board[x][y] = '#';
x = -1, y = -1;
return 1;
}
}
//每一行检查结束后需要计数清零
cnt = 0;
x = -1, y = -1;
}
//是否需要进行堵棋
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
if (board[i][j] == '*') {
cnt1++;
}
if (board[i][j] == ' ') {
x = i, y = j;
}
if (cnt1 == 2 && x != -1 && y != -1) {
cnt = 0;
board[x][y] = '#';
x = -1, y = -1;
return 1;
}
}
cnt1 = 0;
x = -1, y = -1;
}
//判断列
for (int j = 0; j < col; j++) {
for (int i = 0; i < row; i++) {
if (board[i][j] == '#') {
cnt++;
}
if (board[i][j] == ' ') {
x = i, y = j;
}
if (cnt == 2 && x != -1 && y != -1) {
cnt = 0;
board[x][y] = '#';
x = -1, y = -1;
return 1;
}
}
cnt = 0;
x = -1, y = -1;
}
for (int j = 0; j < col; j++) {
for (int i = 0; i < row; i++) {
if (board[i][j] == '*') {
cnt++;
}
if (board[i][j] == ' ') {
x = i, y = j;
}
if (cnt == 2 && x != -1 && y != -1) {
cnt = 0;
board[x][y] = '#';
x = -1, y = -1;
return 1;
}
}
cnt = 0;
x = -1, y = -1;
}
//判断逆对角线
for (int i = 0; i < row; i++) {
if (board[i][row - i - 1] == '#') {
cnt++;
}
if (board[i][row - i - 1] == ' ') {
x = i, y = row - i - 1;
}
if (cnt == 2 && x != -1 && y != -1) {
cnt = 0;
board[x][y] = '#';
x = -1, y = -1;
return 1;
}
}
cnt = 0, x = -1, y = -1;
for (int i = 0; i < row; i++) {
if (board[i][row - i - 1] == '*') {
cnt++;
}
if (board[i][row - i - 1] == ' ') {
x = i, y = row - i - 1;
}
if (cnt == 2 && x != -1 && y != -1) {
cnt = 0;
board[x][y] = '#';
x = -1, y = -1;
return 1;
}
}
cnt = 0, x = -1, y = -1;
//判断逆对角线
for (int i = 0; i < row; i++) {
if (board[i][i] == '#') {
cnt++;
}
if (board[i][i] == ' ') {
x = i, y = row - i - 1;
}
if (cnt == 2 && x != -1 && y != -1) {
cnt = 0;
board[x][y] = '#';
x = -1, y = -1;
return 1;
}
}
cnt = 0, x = -1, y = -1;
for (int i = 0; i < row; i++) {
if (board[i][i] == '*') {
cnt++;
}
if (board[i][i] == ' ') {
x = i, y = row - i - 1;
}
if (cnt == 2 && x != -1 && y != -1) {
cnt = 0;
board[x][y] = '#';
x = -1, y = -1;
return 1;
}
}
cnt = 0, x = -1, y = -1;
//如果上述条件都不满足,则随机下棋
cmp_move(board, row, col);
}
test.c文件
#include<stdio.h>
#include"game.h"
void meue() {
printf("**************************\n");
printf("**************************\n");
printf("****1.Play******0.Exit****\n");
printf("**************************\n");
printf("**************************\n");
}
void game() {
char ret = 0;
char board[ROW][COL];
//初始化棋盘
init_board(board, ROW, COL);
//打印棋盘
print_board(board, ROW, COL);
while (1) {
//玩家下棋
player_move(board, ROW, COL);
print_board(board, ROW, COL);
ret = iswin(board, ROW, COL);
if (ret != 'C') {
break;
}
//电脑下棋
//cmp_move(board, ROW, COL);
intelligence_play(board, ROW, COL);
printf("\n");
print_board(board, ROW, COL);
ret = iswin(board, ROW, COL);
if (ret != 'C') {
break;
}
}
if (ret == '#')
{
printf("电脑Win\n");
}
else if (ret == '*')
{
printf("玩家Win\n");
}
else if (ret == 'Q')
{
printf("平局\n");
}
}
void test() {
srand(time(NULL));
int input = 0;
do {
meue();
printf("请选择:>");
scanf("%d", &input);
switch (input) {
case 1:
game();
break;
case 0:
printf("退出游戏\n");
break;
default:
printf("选择错误,请重新选择\n");
break;
}
} while (input);
}
int main(void) {
test();
return 0;
}