【C语言】三子棋游戏——超细教学

news2024/11/16 13:37:09

🚩纸上得来终觉浅, 绝知此事要躬行。
🌟主页:June-Frost
🚀专栏:C语言

🔥该篇将结合之前的知识来实现 三子棋游戏。

目录:

  • 🌟思路框架:
    • 测试
    • 游戏
  • 🌟测试部分函数实现
  • 🌟游戏部分函数实现
  • 🌟完整的代码:
  • ❤️ 结语

🌟思路框架:

测试

通过迭代保证每次玩完游戏后可以再来一局或者退出。

游戏

博主将会对上图每一个板块进行函数实现。


🌟测试部分函数实现

  • 选择是否玩游戏

该板块的循环部分将会使用do while 循环来实现(保证一开始可以选择,运行完游戏部分后还可以继续选择)。

#include"game.h"

int main()
{
	int input = 0;
	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);
	return 0;
}

🌟游戏部分函数实现

在玩三子棋游戏中,每一步棋下完之后的状态需要保存,即需要数据的保存,所以可以创建一个3X3的数组,之后的数据操作就可以针对数组进行操作。

  • 菜单
void menu()
{
	printf("**************************\n");
	printf("********* 1.play *********\n");
	printf("********* 0.exit *********\n");
	printf("**************************\n");
}
  • 初始化棋盘

定义一个 3X3 数组后,我们将数组的元素都赋为 空格,这样就可以保证打印出来的效果是空的棋盘。

void BoardInit(char board[][Col], int row, int col)
{
	//遍历数组将每个元素赋为 空格
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}
  • 打印棋盘

需要产生的效果:

实现上述的效果,只需要两步拆解:
1.
将这样的棋盘首先分为三部分,每一个部分由数据部分分割线部分构成,分割线部分在最后一部分没有(只需要加一个限制条件即可)

    int i = 0;
	for (i = 0; i < row; i++)
	{
		//打印数据
		
		printf("\n");//打印后换行
		//打印分割线
		if (i < row - 1)//保证最后一部分不打印分割线
		{
	
		}
		printf("\n");//打印后换行
	}


每个部分内部又可以划分为三部分,与第一次拆解逻辑一摸一样,注意第三部分没有 | 即可

void DisplayBoard(char board[][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)
		{
			int j = 0;
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
				{
					printf("|");
				}
			}
		}
		printf("\n");
	}
}
  • 玩家下棋:

这里需要注意 检查玩家输入的坐标是否合法,以及该坐标下是否已经有棋子。而且,在写条件的时候,需要代入玩家视角,输入区间为 1 —— 3,但访问区间为 0——2 。

void PlayerMove(char board[][Col], int row, int col)
{
	printf("玩家下棋:\n");
	printf("请输入下棋的坐标,中间使用空格:>");
	while (1)
	{
		int x = 0;
		int y = 0;
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >=1 && y <= col)//保证棋子能落入棋盘
		{
			if (board[x - 1][y - 1] == ' ')//该坐标下无棋子
			{
				board[x - 1][y - 1] = 'X';//落子
				break;
			}
			else
			{
				printf("该位置已有棋子,请重新输入:>");
			}
		}
		else
		{
			printf("输入非法,请重新输入:>");
		}
	}
}
  • 电脑下棋
    这里需要使用伪随机数,并且直接将范围锁定在 0——2,可以直接访问数组元素。完成该功能只需要判断 坐标下是否有棋子,若有棋子,则重新生成随机数。若要了解随机数,可以参考——随机数 。
void ComputerMove(char board[][Col], int row, int col)
{
	printf("电脑下棋:\n");
	while (1)
	{
		int x = rand() % row;
		int y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = 'O';
			break;
		}
	}
}
  • 判断输赢

只需要从4种方向遍历数组,有符合获胜条件的直接返回数组元素,如果没有获胜,则需要判断是否棋盘已满(平局),除此之外就是未完成该局,继续下棋。

所以返回值 有四种情况:
电脑赢 —— 返回 ‘O’
玩家赢 —— 返回 ‘X’
平局 —— 返回 ‘P’
继续 —— 返回 ‘C’

int IsFull(char board[][Col], int row, int col)//判断是否已满,已满返回1
{
	//遍历
	int  i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')
				return 0;
		}
	}
	return 1;
}
//判断输赢
char IsWin(char board[][Col], int row, int col)
{
	//一方获胜
	//从上到下遍历
	int i = 0;
	for (i = 0; i < row; i++)
	{
		if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
		{
			return board[i][0];
		}
	}
	//从左至右遍历
	for (i = 0; i < col; i++)
	{
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
		{
			return board[0][i];
		}
	}
	//左上至右下
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' ')
	{
		return board[1][1];
	}
	//左下至右上
	if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[2][0] != ' ')
	{
		return board[1][1];
	}
	//平局
	if (IsFull(board,row,col))
	{
		return 'P';
	}
	return 'C';

}
  • 游戏逻辑部分

我们已经有了所有的功能函数,按照思路框架直接写出来即可。

void game()
{
	char board[Row][Col] = { 0 };
	BoardInit(board, Row, Col);
	DisplayBoard(board, Row, Col);
	

	//先手判断
	//假设玩家先手
	void(*Fir)(char board[][Col], int, int) = PlayerMove;
	void(*Sec)(char board[][Col], int, int) = ComputerMove;
	int flag = rand()%2; //1——玩家先手,0——电脑先手
	if (flag == 0)
	{
		printf("电脑先手\n");
		Fir = ComputerMove;
		Sec = PlayerMove;
	}
	else
	{
		printf("玩家先手\n");
	}
	//

	char ret = 0;
	while (1)
	{
		Fir(board, Row, Col);
		DisplayBoard(board, Row, Col);
		ret = IsWin(board, Row, Col);
		if (ret != 'C')
			break;
		Sec(board, Row, Col);
		DisplayBoard(board, Row, Col);
		ret = IsWin(board, Row, Col);
		if (ret != 'C')
			break;
	}
	if (ret == 'X')
	{
		printf("玩家获胜\n");
	}
	else if (ret == 'O')
	{
		printf("电脑获胜\n");
	}
	else
	{
		printf("平局\n");
	}
}

首先假设玩家是先手,如果flag =1,则照旧进行,如果flag = 0,就直接交换先后手。

🌟完整的代码:

test.c :

请记得设置随机数起点srand。

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

int main()
{
	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);
	return 0;
}

game.h

#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define Row 3
#define Col 3
//菜单
void menu();
//游戏(逻辑)
void game();
//初始化棋盘
void BoardInit(char board[][Col], int row, int col);
//打印棋盘
void DisplayBoard(char board[][Col], int row, int col);
//玩家下棋
void PlayerMove(char board[][Col], int row, int col);
//电脑下棋
void ComputerMove(char board[][Col], int row, int col);
//判断输赢
char IsWin(char board[][Col], int row, int col);

game.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"game.h"

void menu()
{
	printf("**************************\n");
	printf("********* 1.play *********\n");
	printf("********* 0.exit *********\n");
	printf("**************************\n");
}

void BoardInit(char board[][Col], int row, int col)
{
	//遍历数组将每个元素赋为 空格
	int i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}

void DisplayBoard(char board[][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)
		{
			int j = 0;
			for (j = 0; j < col; j++)
			{
				printf("---");
				if (j < col - 1)
				{
					printf("|");
				}
			}
		}
		printf("\n");
	}
}

void PlayerMove(char board[][Col], int row, int col)
{
	printf("玩家下棋:\n");
	printf("请输入下棋的坐标,中间使用空格:>");
	while (1)
	{
		int x = 0;
		int y = 0;
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= row && y >=1 && y <= col)//保证棋子能落入棋盘
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = 'X';//落子
				break;
			}
			else
			{
				printf("该位置已有棋子,请重新输入:>");
			}
		}
		else
		{
			printf("输入非法,请重新输入:>");
		}
	}
}

void ComputerMove(char board[][Col], int row, int col)
{
	printf("电脑下棋:\n");
	while (1)
	{
		int x = rand() % row;
		int y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = 'O';
			break;
		}
	}
}

int IsFull(char board[][Col], int row, int col)
{
	//遍历
	int  i = 0;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')
				return 0;
		}
	}
	return 1;
}

char IsWin(char board[][Col], int row, int col)
{
	//一方获胜
	//从上到下遍历
	int i = 0;
	for (i = 0; i < row; i++)
	{
		if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
		{
			return board[i][0];
		}
	}
	//从左至右遍历
	for (i = 0; i < col; i++)
	{
		if (board[0][i] == board[1][i] && board[1][i] == board[2][i] && board[0][i] != ' ')
		{
			return board[0][i];
		}
	}
	//左上至右下
	if (board[0][0] == board[1][1] && board[1][1] == board[2][2] && board[0][0] != ' ')
	{
		return board[1][1];
	}
	//左下至右上
	if (board[2][0] == board[1][1] && board[1][1] == board[0][2] && board[2][0] != ' ')
	{
		return board[1][1];
	}
	//平局
	if (IsFull(board,row,col))
	{
		return 'P';
	}
	return 'C';

}

void game()
{
	char board[Row][Col] = { 0 };
	BoardInit(board, Row, Col);
	DisplayBoard(board, Row, Col);
	

	//先手判断
	//假设玩家先手
	void(*Fir)(char board[][Col], int, int) = PlayerMove;
	void(*Sec)(char board[][Col], int, int) = ComputerMove;
	int flag = rand()%2; //1——玩家先手,0——电脑先手
	if (flag == 0)
	{
		printf("电脑先手\n");
		Fir = ComputerMove;
		Sec = PlayerMove;
	}
	else
	{
		printf("玩家先手\n");
	}
	//

	char ret = 0;
	while (1)
	{
		Fir(board, Row, Col);
		DisplayBoard(board, Row, Col);
		ret = IsWin(board, Row, Col);
		if (ret != 'C')
			break;
		Sec(board, Row, Col);
		DisplayBoard(board, Row, Col);
		ret = IsWin(board, Row, Col);
		if (ret != 'C')
			break;
	}
	if (ret == 'X')
	{
		printf("玩家获胜\n");
	}
	else if (ret == 'O')
	{
		printf("电脑获胜\n");
	}
	else
	{
		printf("平局\n");
	}
}

❤️ 结语

文章到这里就结束了,如果对你有帮助,你的点赞将会是我的最大动力,如果大家有什么问题或者不同的见解,欢迎大家的留言~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/906955.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LangChain + Streamlit + Llama:将对话式AI引入本地机器

推荐&#xff1a;使用 NSDT场景编辑器 助你快速搭建可二次编辑的3D应用场景 什么是LLMS&#xff1f; 大型语言模型 &#xff08;LLM&#xff09; 是指能够生成与人类语言非常相似的文本并以自然方式理解提示的机器学习模型。这些模型使用包括书籍、文章、网站和其他来源在内的…

PCIE 信息

PCIe&#xff08;外围组件互连快件&#xff09;是用于连接高速组件的接口标准。每台台式电脑主板有许多 PCIe 插槽&#xff0c;可用于添加通用显卡&#xff0c;各种外设卡&#xff0c;无线网卡或固态硬盘等等。PC 中可用的 PCIe 插槽类型将取决于你购买的主板. PCIe 插槽有不同…

无需公网IP——搭建web站点

文章目录 概述使用 Raspberry Pi Imager 安装 Raspberry Pi OS设置 Apache Web 服务器测试 web 站点安装静态样例站点将web站点发布到公网安装 Cpolar内网穿透cpolar进行token认证生成cpolar随机域名网址生成cpolar二级子域名将参数保存到cpolar配置文件中测试修改后配置文件配…

9.4 多线程之线程池

线程池 进程已经能做到并发编程了 , 为什么还需要线程 ? 进程实在是太重量了 , 创建和销毁成本都比较高 , 需要申请释放资源 线程就是针对上述问题的优化 , 因为他是共用同一组系统资源的 , 一旦资源申请好了 , 后续就不需要再继续申请了 虽然线程已经很好了 , 不过在更高频…

浙大全日制英文MBA项目提面申请截至9月3日:几乎是上岸必经之路

今年浙大全日制MBA项目提前批面试做了改革&#xff0c;一年只设定一个批次申请&#xff0c;对于考生们来说其实是不太有利的&#xff0c;原因在于整个提面时间节奏会将部分考生逼到进退两难的境地&#xff0c;一方面无提面的状态下全日制MBA项目都知道需要比较高的分数才可能够…

计算机竞赛 基于CNN实现谣言检测 - python 深度学习 机器学习

文章目录 1 前言1.1 背景 2 数据集3 实现过程4 CNN网络实现5 模型训练部分6 模型评估7 预测结果8 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于CNN实现谣言检测 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&am…

GC的前置工作,聊聊GC是如何快速枚举根节点的

本文已收录至GitHub&#xff0c;推荐阅读 &#x1f449; Java随想录 原创不易&#xff0c;注重版权。转载请注明原作者和原文链接 文章目录 什么是根节点枚举根节点枚举存在的问题如何解决根节点枚举的问题安全点安全区域 上篇文章中我们留下了个坑&#xff1a;「 根节点枚举」…

计网第三章(数据链路层)(四)

目录 一、MAC地址和IP地址以及ARP协议 1.MAC地址&#xff1a; 单播MAC地址&#xff1a; 广播MAC地址&#xff1a; 多播MAC地址&#xff1a; 2.IP地址&#xff1a; 3.IP地址和MAC地址&#xff1a; 1.什么情况下使用&#xff1f; 2.IP地址和MAC地址填写位置&#xff1f;…

保障用户观影体验 极米推出首款搭载超级混光技术投影仪极米RS Pro 3

随着投影市场的日渐蓬勃&#xff0c;倒推了投影技术的研发创新&#xff0c;投影仪产品在最近几年已经逐步进入到家庭空间&#xff0c;成为很多家庭不可或缺的家电产品&#xff0c;不论是客厅还是卧室&#xff0c;或者是户外露营等&#xff0c;投影仪的家用场景已经拓展到了人们…

ps2024后期调色滤镜插件使用教程图解

Exposure是一款常见的ps调色滤镜插件&#xff0c;相信许多朋友都曾经用过它。一个优秀的摄像师不仅需要懂得摄像技巧&#xff0c;还要掌握图像的后期调色窍门。那么&#xff0c;后期调色软件哪个好&#xff0c;后期调色教程图解&#xff0c;接下来一起来了解一下吧。 一、后期…

为何Ping不通啊?

为什么Ping不通啊&#xff01;求指点&#xff1f; 友情提醒&#xff0c;这个问题难度系数初级难度&#xff08;CCNA&#xff09;&#xff0c;只适合初级读者阅读&#xff0c;避免浪费宝贵时间。 Q1: 为何PC1能Ping通PC2&#xff1f;或反之亦然&#xff1f; PC1主观认为PC2和自…

YOLO目标检测——血管瘤数据集下载分享

血管瘤数据集在医学领域的研究和临床应用中起着重要的作用。它可以用于开发和评估血管瘤检测和分割算法&#xff0c;帮助医生准确地定位和诊断血管瘤。此外&#xff0c;血管瘤数据集还可以用于研究血管瘤的生长规律、预测血管瘤的发展趋势等。 数据集点击下载&#xff1a;血管瘤…

云windows iis部署vue前端项目 步骤

工作记录 第一步 先进行vue项目打包 npm run build打包后会产生一个dist文件 然后将前端全部文件传到想要部署的windows上 这个一步直接远程连接 复制粘贴就可以 第二步 打开iis 添加网络 网站名称输入你想要用的二级域名 &#xff08;需要到**云上报备下 我这个用的是阿里…

自动化测试工具Selenium的语法续.

OK&#xff0c;那么上篇博客我们介绍了如何搭建基于Javaselenium的环境&#xff0c;并且使用selenium的一些语法给大家演示了如何进行自动化测试的案例&#xff0c;那么本篇博客我们来继续学习selenium的一些其他的比较重要的语法&#xff0c;感谢关注&#xff0c;期待三连~ 目…

快速学会创建uni-app项目并了解pages.json文件

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 前言 创建 uni-app 项目 通过 HBuilderX 创建 pages.json pages style globalStyle tabBar 前言…

从零做软件开发项目系列之二——需求调研

在接到软件开发任务之后&#xff0c;第一件要做的事情就是进行需求调研工作&#xff0c;基于前期的沟通以及合同向用户了解具体需求&#xff0c;从而有针对性地开展后续工作。整个调研过程分为调研准备&#xff0c;调研实施&#xff0c;需求分析。 1 调研准备 俗话说&#x…

基于微信小程序的宠物领养平台的设计与实现(Java+spring boot+微信小程序+MySQL)

获取源码或者论文请私信博主 演示视频&#xff1a; 基于微信小程序的宠物领养平台的设计与实现&#xff08;Javaspring boot微信小程序MySQL&#xff09; 使用技术&#xff1a; 前端&#xff1a;html css javascript jQuery ajax thymeleaf 微信小程序 后端&#xff1a;Java…

【工程实践】使用pandas的记录

前言 工作中处理数据时&#xff0c;经常需要使用pandas&#xff0c;记录一些工作中使用pandas的习惯。 1. 合并数据 #读取原始数据 data1 pd.read_excel(/home/zhenhengdong/WORk/Classfier/Dates/Original/1.xlsx) data2 pd.read_excel(/home/zhenhengdong/WORk/Classfier/…

石油化工钻井消防vr虚拟教学实训增加学员参与感

随着石油化工企业的生产规模和数量不断扩大&#xff0c;石油化工火灾事故常有发生&#xff0c;给企业及当地居民造成难以挽回的严重损失&#xff0c;为了避免石油化工火灾安全事故发生&#xff0c;VR安全培训公司深圳华锐视点制作的石油化工火灾vr模拟应急救援系统&#xff0c;…

java+springboot+mysql小区自来水实时监控管理系统

项目介绍&#xff1a; 使用javaspringbootmysql开发的小区自来水实时监控管理系统&#xff0c;系统包含超级管理员&#xff0c;系统管理员、用户角色&#xff0c;功能如下&#xff1a; 超级管理员&#xff1a;管理员管理&#xff1b;楼栋管理&#xff1b;租户管理、用水管理&…