<C语言>代码实现与自己创造的“人工智障”进行三子棋对弈,并且艰难的让电脑赢!

发布于:2022-11-08 ⋅ 阅读:(775) ⋅ 点赞:(0)

目录

文章目录

前言

一、创建文件项目

二、具体实现步骤

1.呈现菜单,并实现玩家可一直游玩游戏

2.选择进入游戏后,打印棋盘

 2.1初始化棋盘

 2.2打印棋盘

3.打印棋盘后,玩家和电脑开始轮番下棋

3.1 实现玩家下棋,棋子为 *

3.2 电脑下棋,棋子为 #

4.判断输赢

5.整理代码

总结



前言

        注意看,这个男人叫小帅,现在,他正在敲代码实现和机器人下三子棋,终于小帅敲好了代码,他当即与机器人下了一局,然而小帅发现,这机器人竟然是......


一、创建文件项目

        时间回到小帅写代码时的第一步,小帅首先创建项目以text.c为核心源文件通过调用game.c和game.h内的函数实现三子棋的所有功能。

二、三子棋具体实现步骤

1.呈现菜单,并实现玩家可一直游玩游戏

1:代表进入游戏

0:代表退出游戏

        菜单函数的代码如下(示例):

//text.c源文件
#include<stdio.h>
void menu()//定义菜单函数
{
	printf("*****************************\n");
	printf("************1.play***********\n");
	printf("************0.exit***********\n");
	printf("*****************************\n");
}
void test()
{
	int input = 0;
	do//该循环实现玩家可以一直游玩三子棋
	{
		menu();//调用菜单函数
		printf("请选择:>");
		scanf("%d", &input);//请玩家选择进入游戏or退出游戏
		switch (input)      //采用switch语句较为便捷
		{
		case 1:
			printf("三子棋\n");
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择:\n");
			break;
		}
	} while (input);//0为假,非0为真,即进入游戏输入1,1作为判断条件为真,游戏继续
}
int main()
{
	
	test();
	return 0;
}

        效果: 

2.选择进入游戏后,打印棋盘

小帅的目标:打印九宫格简易棋盘,并且能够在每个指定格子内存放棋子(即数据)

 2.1初始化棋盘

        小帅的“头脑风暴”1: 要在九宫格内存放棋子还要打印在屏幕上,我应该用3*3大小的二维数组 ,将每个数组元素发在九宫格的每个格子中间,但是呢,要注意,如果我只将棋盘的形状打印屏幕上,后续玩家在指定下棋时棋子是不能覆盖原先屏幕上的符号的,所以我在打印棋盘时就应该保留各个棋子应存放的空间,并存放空格来等待玩家或电脑下的棋子。emm,就是这样!

        为了使后续棋盘大小不局限于 3行3列 大小,小帅宏定义ROW 、 COL,如果后续想要改动棋盘大小仅需改变宏定义的ROW 、COL大小即可。宏定义名称与值时替换关系。

       加上初始化函数的代码如下(示例):

//game.h头文件
#define ROW 3
#define COL 3
#include<stdio.h>
void InitBoard(char board[ROW][COL], int row, int col);
//game.c源文件
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"//调用自定义头文件
void InitBoard(char board[ROW][COL], int row, int col)//定义初始化棋盘函数
{
	int i = 0, j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}
//text.c源文件
#include "game.h"//调用自定义的头文件

void game()//定义game函数
{
	//初始化棋盘,用函数实现
    //传入数组、行数、列数,以便初始化
	InitBoard(board, ROW, COL);//数组内全部空格
}                                 
void test()
{
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("三子棋\n");
			game();//游戏的实现!
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择:\n");
			break;
		}
	} while (input);
}
int main()
{
	
	test();
	return 0;
}

2.2打印棋盘

        小帅发现

棋盘第一行为:

                空格 %c 空格 | 空格 %c 空格 | 空格 %c 空格

最小重复单元,发现为:空格 %c 空格 |要注意的是第三个 | 不需要打印,用if判断即可

第二行为:

                - - - | - - - | - - -

最小重复单元,发现为:- - - |,同样的,要注意的是第三个 | 不需要打印,用条件控制一下即可

并且最后一行无需打印:  - - - | - - - | - - -

将以上推断结果封装为打印函数

        加上打印函数的代码如下(示例):

//game.h头文件
#define ROW 3
#define COL 3
#include<stdio.h>
void InitBoard(char board[ROW][COL], int row, int col);
void DisplayBoard(char board[ROW][COL], int row, int col);
//game.c源文件
#define _CRT_SECURE_NO_WARNINGS
#include "game.h"
void InitBoard(char board[ROW][COL], int row, int col)//定义//初始化棋盘函数
{
	int i = 0, j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}
void DisplayBoard(char board[ROW][COL], int row, int col)//定义打印棋盘函数
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		//1.打印一行数据
		for (j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);//空格 棋子 空格
			if (j < col - 1)
				printf("|");
		}
		printf("\n");
		//2.打印分割行
		if (i < row - 1)
		{
			for (j = 0; j < col; j++)
			{
				printf("---");
				if(j<col-1)
					printf("|");
			}
			printf("\n");
		}
	}
}
//text.c源文件
#include "game.h"//调用自定义的头文件
void game()//定义game函数
{
	//初始化棋盘,用函数实现   并传入数组、行数、列数,以便初始化
	InitBoard(board, ROW, COL);//数组内全部空格
    //打印棋盘
    DisplayBoard(board, ROW, COL);
}                                 
void test()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("三子棋\n");
			game();//游戏的实现!
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择:\n");
			break;
		}
	} while (input);
}
int main()
{
	
	test();
	return 0;
}

最后每当玩家或电脑下棋后都要更新打印一次棋盘 

3.打印棋盘后,玩家和电脑开始轮番下棋

3.1 实现玩家下棋,棋子为 *

小帅的“头脑风暴”2:

下棋实质上是为二维数组赋值,而我们赋值时就有了诸多限制:

  1. 我要下的格子内是否已经被下过了?
  2. 非编程玩家是否知道二维数组第一行和第一列下标是从0开始?
  3. 玩家给出的行和列是否超过所给棋盘的大小了?

 

 无所谓,小帅会出手!

所以,我们要解决以上问题

  1. 判断要下的格子内是否已经被下过了,可以判断该数组元素是否为空格,因为我们之前已经初始化所有数组内容为空格,一旦有棋子落下,该数组元素就会被赋以棋子符号,就不为空格。
  2. 玩家默认第一行和列都从1开始,那么我们将玩家输入的行列数减一即可
  3. 这里我们就要有限制条件,玩家给出的行和列应小于等于棋盘的行列数,并且大于等于1。如果不合法,进行提示。

        玩家下棋函数的代码如下(示例):


void PlayerMove(char board[ROW][COL], int row, int col)
{
	int x = 0, y = 0;
	printf("玩家走:>\n");
	while (1)

	{
		printf("请输入要下的坐标:>");
		scanf("%d%d", &x, &y);
		//判断xy坐标的合法性
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("该坐标被占用\n");
			}
		}
		else
		{
			printf("坐标非法,请重新输入!\n");
		}
	}
}

将玩家下棋函数声明、函数调用、函数定义分别放至game.h、test.c中的game()函数中、game.c中即可。这里为了避免篇幅过长将最后展现全部代码。 

3.2 电脑下棋,棋子为 #

小帅让电脑随机下棋,使用srand(time(NULL)),将rand()%row赋值给电脑下棋的行数,将rand()%col赋值给电脑下棋的列数。

        电脑下棋函数的代码如下(示例)

void ComputerMove(char board[ROW][COL], int row, int col)
{
	int x = 0, y = 0;
	printf("电脑走:>\n");
	while (1)
	{
		x = rand() % row;
		y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

 将电脑下棋函数声明、函数调用、函数定义分别放至game.h、test.c中的game()函数中、game.c中即可,将srand(time(NULL))放至test.c的test()函数中。这里为了避免篇幅过长将最后展现全部代码。 

4.判断输赢

小帅的头脑风暴3:

定义判断输赢函数,在玩家和电脑每次下棋后都要判断一次输赢

  1. 电脑赢返回 #
  2. 玩家赢返回 *
  3. 平局返回 Q
  4. 游戏继续返回 C

玩家或电脑赢需要满足三个棋子相同并且连成一条线,注意排除空格情况:

  1. 同一行
  2. 同一列
  3. 对角线

平局情况只需要判断棋盘是否还剩有空格即可

       判断输赢函数代码如下(示例):

//返回1表示棋盘满了
//返回0表示棋盘没满
int IsFull(char board[ROW][COL], int row, int col)
{
	int i = 0, j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')
				return 0;
		}
	}
	return 1;
}



char Iswin(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;
	//横三行
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == board[i][j+1] && board[i][j+1] == board[i][j+2] && board[i][j] != ' ')
			{
				return board[i][j];
			}
		}
	}
	//竖三列
	for (i = 0; i < col; i++)
	{
		for (j = 0; j < row; j++)
		{
			if (board[j][i] == board[j+1][i] && board[j+1][i] == board[j+2][i] && board[j][i] != ' ')
			{
				return board[j][i];
			}
		}
	}
	//两个对角线
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
	{
		return board[0][0];
	}
	if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] != ' ')
	{
		return board[1][1];
	}
	//判断是否平局
	if (1 == IsFull(board, ROW, COL))
	{
		return 'Q';
	}
	else
		return 'C';
}

 将输赢判断函数声明、函数调用、函数定义分别放至game.h、test.c中的game()函数中、game.c中即可。无所谓,小帅会继续出手!

5.整理代码

//game.h头文件
#define ROW 3
#define COL 3
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
//声明
void InitBoard(char board[ROW][COL], int row, int col);
void DisplayBoard(char board[ROW][COL], int row, int col);
void PlayerMove(char board[ROW][COL],int row,int col);
void ComputerMove(char board[ROW][COL],int row,int col);
//告诉我们四种状态
//玩家赢--'*
//电脑赢--'#
//平局----'Q'
//继续----'C'
char Iswin(char board[ROW][COL], int row, int col);
//game.c源文件
#define _CRT_SECURE_NO_WARNINGS 1

#include "game.h"
void InitBoard(char board[ROW][COL], int row, int col)
{
	int i = 0, j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}
void DisplayBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		//1.打印一行数据
		for (j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1)
				printf("|");
		}
		printf("\n");
		//2.打印分割行
		if (i < row - 1)
		{
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
					printf("|");
			}
			printf("\n");
		}
	}
}
void PlayerMove(char board[ROW][COL], int row, int col)
{
	int x = 0, y = 0;
	printf("玩家走:>\n");
	while (1)

	{
		printf("请输入要下的坐标:>");
		scanf("%d%d", &x, &y);
		//判断xy坐标的合法性
		if (x >= 1 && x <= row && y >= 1 && y <= col)
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("该坐标被占用\n");
			}
		}
		else
		{
			printf("坐标非法,请重新输入!\n");
		}
	}
}
void ComputerMove(char board[ROW][COL], int row, int col)
{
	int x = 0, y = 0;
	printf("电脑走:>\n");
	while (1)
	{
		x = rand() % row;
		y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}
//返回1表示棋盘满了
//返回0表示棋盘没满
int IsFull(char board[ROW][COL], int row, int col)
{
	int i = 0, j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')
				return 0;
		}
	}
	return 1;
}



char Iswin(char board[ROW][COL], int row, int col)
{
	int i = 0;
	int j = 0;
	//横三行
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == board[i][j+1] && board[i][j+1] == board[i][j+2] && board[i][j] != ' ')
			{
				return board[i][j];
			}
		}
	}
	//竖三列
	for (i = 0; i < col; i++)
	{
		for (j = 0; j < row; j++)
		{
			if (board[j][i] == board[j+1][i] && board[j+1][i] == board[j+2][i] && board[j][i] != ' ')
			{
				return board[j][i];
			}
		}
	}
	//两个对角线
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[1][1] != ' ')
	{
		return board[0][0];
	}
	if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[1][1] != ' ')
	{
		return board[1][1];
	}
	//判断是否平局
	if (1 == IsFull(board, ROW, COL))
	{
		return 'Q';
	}
	else
		return 'C';
}
//text.c源文件
#define _CRT_SECURE_NO_WARNINGS
//测试三子棋游戏
#include "game.h"
#include<stdio.h>
void menu()
{
	printf("*****************************\n");
	printf("************1.play***********\n");
	printf("************0.exit***********\n");
	printf("*****************************\n");
}


//游戏的整个算法实现
void game()
{
	char ret = 0;
	//数组-存放走出的棋盘信息
	char board[ROW][COL] = { ' ' };//数组内全部空格
	//初始化棋盘,用函数实现
	InitBoard(board, ROW, COL);
	//打印棋盘
	DisplayBoard(board, ROW, COL);
	//下棋
	while (1)
	{
		//玩家下棋
		PlayerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);
		//判断玩家是否赢
		ret = Iswin(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
		//电脑下棋
		ComputerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);
		//判断电脑是否赢
		ret = Iswin(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
	}
	if (ret == '*')
	{
		printf("玩家赢\n");
	}
	else if (ret == '#')
	{
		printf("电脑赢\n");
	}
	else if (ret == 'Q')
	{
		printf("平局\n");
	}
}
void test()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("三子棋\n");
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,请重新选择:\n");
			break;
		}
	} while (input);
}
int main()
{

	test();
	return 0;
}

效果 


总结

        小帅的代码结束,发现自己实现的电脑竟然是个人工智障(给它机会它都不中用的那种),所以我们发现这里有诸多待优化的点,例如:

  1. 判断三连子时,棋盘大小不为3*3,那该如何判断对角线三连子?
  2. 我们是否可以优化电脑,赋以人性,让它有玩家的思维赢下玩家?

小帅把问题留给我们,这次...他腾不出手了

        最后,如果小伙伴们有优化的方案,请在评论区留言(ps:抱大佬的腿),新手创作,实属不易,如果满意,还请给个免费的赞,三连也不是不可以(流口水幻想),嘿!那我们下期再见喽,拜拜!

 

本文含有隐藏内容,请 开通VIP 后查看