IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言初学者的第一个小项目:五子棋(附带电脑下棋小算法) -> 正文阅读

[C++知识库]C语言初学者的第一个小项目:五子棋(附带电脑下棋小算法)

C语言初学者的第一个小项目,瑕疵很多,欢迎大佬指点
棋盘绘制部分参考大佬文章:C语言实现的五子棋

效果图:
五子棋
大致说一下关键点:

1.判断输赢

1)一场游戏的结果只会被一个新落下的棋子改变(貌似废话),所以这里并不需要每次都遍历棋盘所有棋子,只需要判断新落下棋子是否会在四个方向上形成5连珠的情况.

2)分为 上,下,左,右,左上,右下,右上,左下,8个方向分别判断连续的个数,(其中,上,下方向 使用同一个变量进行计数,最后表示纵向连续的数量,其他的几个方向也如此),最后取四个计数的最大值,如果大于等于4,即表示5连珠.

2.电脑下棋算法(一点点小小的想法)

模拟一下真人下棋的思维,首先会先看自己棋子下哪个位置棋子连起来的多,其次看哪个位置对手连起来的多,如果对手棋子连续很多,就要进行防守.

程序直接计算每个位置 自己棋子棋子连续的数量和对方棋子连续数量之和,结果最大的,作为棋子最佳落点.
但是这样计算出来的效果会让程序傻乎乎的在该防守的时候不知道防守.

于是我把公式中对方棋子数量乘以两倍,这样即使我有两个棋子连续,对方只有一个棋子,那也会去防守对方.

这样的效果是至少能让电脑陪你玩很多轮,有了一定的游戏体验.

/***************************************************************
	> File Name: c/wu_zi_qi_2.0.c
	> Author: Wgq
	> Created Time: 2021年07月12日 星期一 18时48分49秒
 **************************************************************/

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#include<unistd.h>

#define SIZE 20  //棋盘大小
#define RIGHT (i>=2&&i<=SIZE-1&&j>=2&&j<=SIZE-1)//越界条件

//画图字符:┬┐├┼┤└┴┘│─●○

int board[SIZE][SIZE]={0};   //棋盘 0:无棋子 1:黑棋子 2:白棋子
int computer=0;         //人机开启标识符

void print_board(){         //打印棋盘
	printf("  ");
	for(int i=0;i<SIZE;i++){
		printf("%d",i+1);
		if(i<8)
			printf(" ");
	}
	printf("\n");
	for(int i=0;i<SIZE;i++){
		for(int j=0;j<SIZE;j++){
			if(i==0){
				if(j==0){
					if(board[i][j]==1)
						printf(" 1○─");
					else if(board[i][j]==2)
						printf(" 1●─");
					else
						printf(" 1┌─");
				}
				else if(j==SIZE-1){
					if(board[i][j]==1){
						printf("○");
						printf("\n");
					}
					else if(board[i][j]==2){
						printf("●");
						printf("\n");
					}
					else{
						printf("┐");
						printf("\n");
					}
				}
				else{
					if(board[i][j]==1)
						printf("○─");
					else if(board[i][j]==2)
						printf("●─");
					else
						printf("┬─");
				}
			}
			else if(i>0&i<SIZE-1){
				if(j==0){
					if(i<9)
						printf(" ");
					if(board[i][j]==1)
						printf("%d○─",i+1);
					else if(board[i][j]==2)
						printf("%d●─",i+1);
					else
						printf("%d├─",i+1);
				}
				else if(j==SIZE-1){
					if(board[i][j]==1){
						printf("○");
						printf("\n");
					}
					else if(board[i][j]==2){
						printf("●");
						printf("\n");
					}
					else{
						printf("┤");
						printf("\n");
					}
				}
				else{
					if(board[i][j]==1)
						printf("○─");
					else if(board[i][j]==2)
						printf("●─");
					else
						printf("┼─");
				}
			}
			else{
				if(j==0){
					if(board[i][j]==1)
						printf("%d○─",SIZE);
					else if(board[i][j]==2)
						printf("%d●─",SIZE);
					else
						printf("%d└─",SIZE);
				}
				else if(j==SIZE-1){
					if(board[i][j]==1){
						printf("○");
						printf("\n");
					}
					else if(board[i][j]==2){
						printf("●");
						printf("\n");
					}
					else{
						printf("┘");
						printf("\n");
					}
				}
				else{
					if(board[i][j]==1)
						printf("○─");
					else if(board[i][j]==2)
						printf("●─");
					else
						printf("┴─");
				}
			}
		}
	}
} 
void put_white(int x,int y){     //放置白棋子
	board[x-1][y-1]=2;
}
void put_black(int x,int y){     //放置黑棋子
	board[x-1][y-1]=1;
}
int get_chess(int x,int y){      //获取棋盘内容
	return board[x-1][y-1];
}
int who_first(){                 //决定谁先
	srand(time(NULL));              
	if(rand()%10<5) return 1;    // 随机数0-9    1/2概率返回1
	else            return 0;      
}
int traversal_black(int x,int y){    //遍历黑棋子布局
	int i,j,max;                   
	int udsame=0,lrsame=0,ldsame=0,rdsame=0;    //分别为 上下方向 左右方向 左对角线方向 右对角线方向
	i=x;j=y;                                    //棋子连续相同的个数
	while(RIGHT&&get_chess(--i,j)==1){    //up
		udsame++;
	}
	i=x;j=y;                             //用i j读x,y值  对i,j进行赋值操作 不破坏x,y值
	while(RIGHT&&get_chess(++i,j)==1){    //down
		udsame++;
	}
	i=x;j=y;
	while(RIGHT&&get_chess(i,--j)==1){    //left
		lrsame++;
	}
	i=x;j=y;
	while(RIGHT&&get_chess(i,++j)==1){    //right
		lrsame++;
	}
	i=x;j=y;
	while(RIGHT&&get_chess(--i,++j)==1){    //右上
		ldsame++;
	}
	i=x;j=y;
	while(RIGHT&&get_chess(++i,--j)==1){    //左下
		ldsame++;
	}
	i=x;j=y;
	while(RIGHT&&get_chess(--i,--j)==1){    //左上
		rdsame++;
	}
	i=x;j=y;
	while(RIGHT&&get_chess(++i,++j)==1){    //右下
		rdsame++;
	}
	max=udsame;
	if(lrsame>max) max=lrsame;
	if(ldsame>max) max=ldsame;
	if(rdsame>max) max=rdsame;    //求出各方向棋子连续个数的最大值 最大值超过3即视为胜利
	return max;
}
int traversal_white(int x,int y){  //遍历白色棋子
	int i,j,max;
	int udsame=0,lrsame=0,ldsame=0,rdsame=0;
	i=x;j=y;
	while(RIGHT&&get_chess(--i,j)==2){    //up
		udsame++;
	}
	i=x;j=y;
	while(RIGHT&&get_chess(++i,j)==2){    //down
		udsame++;
	}
	i=x;j=y;
	while(RIGHT&&get_chess(i,--j)==2){    //left
		lrsame++;
	}
	i=x;j=y;
	while(RIGHT&&get_chess(i,++j)==2){    //right
		lrsame++;
	}
	i=x;j=y;
	while(RIGHT&&get_chess(--i,++j)==2){    //右上
		ldsame++;
	}
	i=x;j=y;
	while(RIGHT&&get_chess(++i,--j)==2){    //左下
		ldsame++;
	}
	i=x;j=y;
	while(RIGHT&&get_chess(--i,--j)==2){    //左上
		rdsame++;
	}
	i=x;j=y;
	while(RIGHT&&get_chess(++i,++j)==2){    //右下
		rdsame++;
	}
	max=udsame;
	if(lrsame>max) max=lrsame;
	if(ldsame>max) max=ldsame;
	if(rdsame>max) max=rdsame;
	return max;
}
void menu(){
	int input;
	printf("*******************************************\n");
	printf("                 五子棋\n");
	printf("              1 人机对战\n");
	printf("              2 双人游戏\n");
	printf("              3 退出游戏\n");
	printf("*******************************************\n");
	do{
		printf("请输入选项 : ");
		scanf("%d",&input);
		getchar();      //恰掉回车
	    if(input!=1&&input!=2&&input!=3) 
			printf("输入错误请重新输入:\n");
	}while(input!=1&&input!=2&&input!=3);    //输入错误 
	if(input==3) exit(0);	//输入2 直接结束主程序
	if(input==1) computer=1;      //人机对战 开启人机标识符
}

int *cpu_think(){
	int best_pos[2]={10,10};//默认落点(10,10)
	int *p=best_pos;      //用指针将两个数带回主函数
	int score=0;          //评判每个点的分数 初始为0
	for(int i=1;i<=20;i++){    //遍历整个棋盘
		for(int j=1;j<=20;j++){ //棋盘每个点白棋子和黑棋子各自连续的个数 两者线性组合 作为评分标准
			if(score<(9*traversal_white(i,j)+10*traversal_black(i,j))&&get_chess(i,j)==0){  //黑棋子权重为2 将重点防守黑棋子的落子
				score=9*traversal_white(i,j)+10*traversal_black(i,j);       //求出棋盘各点评分最高的点
				*p=i;*(p+1)=j;
			}
			if(traversal_black(i,j)>3&&get_chess(i,j)==0){    
                *p=i;*(p+1)=j;                              
				return p;
			}
			if(traversal_white(i,j)>3&&get_chess(i,j)==0){    //如果在防守过程中 出现四个连续白色棋子 将直接返回该点坐标
				*p=i;*(p+1)=j;                               //不会总是防守黑棋子 
				return p;
			}
			if(traversal_white(i,j)>2&&get_chess(i,j)==0){   
				*p=i;*(p+1)=j;                               
				return p;
			}
		}
	}
	return p;
}
int main(){
	int x,y;
	int first=who_first();// 抛个骰子
	menu();             //加载菜单
	system("clear");
	print_board();     //打印初始棋盘
	printf("输入(int)棋子坐标x y (以空格隔开)来放置棋子\n");
	printf("输入两个0(以空格隔开)退出游戏\n");
    if(first)
		printf("黑棋先手\n");
	else
		printf("白棋先手\n");
	while(1){                    // 1/2概率first=0 如果等于0 则黑棋“被屏蔽一次输入 变为白棋先手
		if(first){          //否则 程序顺序执行 ”O“先手
			do{                                         //黑棋下棋
			 	printf("输入黑棋坐标x y 以空格隔开 : ");               
				scanf("%d %d",&x,&y);
				getchar();
				if(x==0&&y==0) return 0;    //输入两个0 退出游戏
				if(x<1||x>SIZE||y<1||y>SIZE||get_chess(x,y)!=0) 
	 				printf("输入错误,重新输入:\n");
			}while(x<1||x>SIZE||y<1||y>SIZE||get_chess(x,y)!=0);     //输入检测 数值越界或该棋盘位置不为空 则重新输入
			printf("黑棋落子()%d,%d)",x,y);
			put_black(x,y);         //放黑棋子到(x,y)坐标位置
			system("clear");
			print_board();         //放置后刷新打印棋盘
			if(traversal_black(x,y)>3){    //遍历结果大于3 即结束游戏
				puts("黑棋胜!");
				return 1;
			}	
        }
		first=1;    //屏蔽一次之后即取消屏蔽 否则黑棋永远不下棋
		if(!computer){       //人机标识符开启 将屏蔽手动输入
			do{                                     //白棋下棋
				printf("输入白棋坐标x y 以空格隔开: ");    
				scanf("%d %d",&x,&y);
				getchar();
				if(x==0&&y==0) return 0;    //输入两个0 退出游戏
				if(x<1||x>SIZE||y<1||y>SIZE||get_chess(x,y)!=0) 
					printf("输入错误,重新输入:\n");
			}while(x<1||x>SIZE||y<1||y>SIZE||get_chess(x,y)!=0);
			printf("白棋落子(%d,%d)\n",x,y);
		}
		if(computer){     //人机标识符开始 将调用人机函数返回落点信息
			x=*(cpu_think());
			y=*(cpu_think()+1);
			printf("电脑落子(%d,%d)\n",x,y);
		}
        put_white(x,y);
        system("clear");
		print_board();
		if(traversal_white(x,y)>3){
			puts("白棋胜 !");
			return 1;
		}
	}
	return 0;
}

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-16 11:33:49  更:2021-08-16 11:34:47 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年12日历 -2024/12/26 16:19:28-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码
数据统计