C语言小游戏--三子棋

news2025/1/13 13:06:26

目录

问题描述

逻辑分析

具体实现

1.进入菜单界面

2.初始化棋盘

3.打印棋盘

4.玩家下棋

5.电脑下棋

6.判断输赢

运行结果

完整代码

game.h

game.c

test.c


问题描述

结合C语言所学知识,简单实现一个三子棋小游戏。

逻辑分析

  1. 进入菜单界面
  2. 初始化棋盘
  3. 打印棋盘
  4. 玩家下棋
  5. 判断输赢
  6. 电脑下棋
  7. 判断输赢
  8. 游戏结束

具体实现

1.进入菜单界面

玩家通过输入1或者0来选择进入对应的环节:输入“1”则进入游戏环节;输入“0”则退出游戏;如果输入其他数字,则会提示“选择错误”

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);

		switch (input)
		{
		case 1:
			//printf("三子棋\n");
			game();//游戏
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误\n");
			break;
		}
	} while (input);

}

运行结果:

2.初始化棋盘

棋盘格式设置为3*3的二维数组大小,元素类型为char,初始化为空格,即什么数据也不存储。行列采用宏定义的方式,是为了方便后期对棋盘的大小进行统一修改,同时也为了提高代码的可读性

#define ROW 3
#define COL 3

//初始化棋盘为全空格
void InitBoard(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++)
		{
			board[i][j] = ' ';
		}
	}
}

3.打印棋盘

将棋盘进行拆分,第一行为: 字符 | 字符 | 字符 (注意字符前后的空格),第二行我们为:---|---|---。在打印数据时,根据for循环,打印三行数据,也就是三行字符 | 字符 | 字符 ,同时三行数据中穿插着两行---|---|---作为分隔。

//打印棋盘
void DisplayBoard(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.玩家下棋

玩家根据提示输入行列坐标,比如1 1,同时使用’*’表示玩家落子。玩家在落子之前需要对坐标进行合法性的判断,即判断所输入的坐标是否在棋盘规定的范围内以及是否已被占用。玩家要选择在棋盘上空闲的位置进行落子,否则会提示出错。输入坐标合法之后要立即跳出循环,否则会陷入死循环。

//玩家下棋
void player_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("玩家下棋\n");

	while (1)
	{
		printf("请输入坐标:>");
		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] = '*';
				break;//不加break会进入死循环
			}
			else
			{
				printf("该坐标被占用,请重新输入\n");
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}
	}
}

运行结果:

5.电脑下棋

在玩家落子之后,电脑会随机生成行列坐标并在棋盘上空闲的地方进行相应的落子,'#’表示电脑落子。生成随机数之前需要基于当前时间设定“种子”的值srand((unsigned int)time(0))。在调用rand()函数之前,要先在主函数中调用srand函数。如果没有上述准备工作,那么种子的值就会默认为1,会生成相同的随机数序列。

void computer_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("电脑下棋:>\n");

	while (1)
	{
		x = rand() % row;//0-2
		y = rand() % col;//0-2

		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;//跳出循环
		}
	}
}

运行结果:

6.判断输赢

在判断输赢时,我们约定:‘*’ 表示玩家赢;‘#’ 表示电脑赢;’ Q’ 表示平局;‘C’ 表示继续

玩家赢:行,列以及两条对角线有三个连续的‘*’则表示玩家赢

电脑赢:行,列以及两条对角线有三个连续的‘#’则表示电脑赢

平局:当棋盘中不存在连续三个‘#’或者‘*’时,并且棋盘已满,则表示平局

继续:当棋盘中不存在连续三个‘#’或者‘*’时,并且棋盘未满,则游戏继续

//判断平局
static int is_full(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++)
		{
			if (board[i][j] == ' ')
			{
				return 0;//没满
			}
		}
	}

	return 1;//满了
}


//判断输赢
char is_win(char board[ROW][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][1]!= ' ')
		{
			return board[i][1];
		}
	}

	//判断列
	for (i = 0; i < col; i++)
	{
		if (board[0][i]==board[1][i] && board[1][i]==board[2][i] && board[1][i] != ' ')
		{
			return board[1][i];
		}
	}

	//判断对角线
	if (board[0][0]==board[1][1] && board[1][1]==board[2][2] && board[1][1] != ' ')
	{
		return board[1][1];
	}
	if (board[0][2]==board[1][1] && board[1][1]== board[2][0] && board[1][1] != ' ')
	{
		return board[1][1];
	}

	//判断平局
	if (is_full(board, row, col) == 1)
	{
		return 'Q';
	}

	//继续
	return 'C';
}

运行结果

玩家赢:

 电脑赢:

平局:

完整代码

game.h

#define _CRT_SECURE_NO_WARNINGS 1

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

#define ROW 3
#define COL 3

//初始化棋盘为全空格
void InitBoard(char board[ROW][COL],int row,int col);

//打印棋盘
void DisplayBoard(char board[ROW][COL], int row, int col);

//玩家下棋
void player_move(char board[ROW][COL], int row, int col);

//电脑下棋
void computer_move(char board[ROW][COL], int row, int col);

//判断输赢
char is_win(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;
	for (i = 0; i < row; i++)
	{
		int j = 0;
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}


//打印棋盘
//版本1.0
//void DisplayBoard(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]);//打印的是棋盘初始化中的空格
//		}
//		printf("\n");
//	}
//}

//版本2.0
//存在缺陷,行数可以改变,但是列数被固定死无法修改
//void DisplayBoard(char board[ROW][COL], int row, int col)
//{
//	int i = 0;
//	for (i = 0; i < row; i++)
//	{
//		//打印数据
//		printf(" %c | %c | %c \n",board[i][0],board[i][1],board[i][2]);//存在缺陷,行数可以改变,但是列数被固定死无法修改
//
//		//打印分割行
//		if (i < row - 1)//最后一行的分割行不打印
//		{
//			printf("---|---|---\n");
//		}
//	}
//}

//版本3.0
void DisplayBoard(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)
{
	int x = 0;
	int y = 0;
	printf("玩家下棋\n");

	while (1)
	{
		printf("请输入坐标:>");
		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] = '*';
				break;//不加break会进入死循环
			}
			else
			{
				printf("该坐标被占用,请重新输入\n");
			}
		}
		else
		{
			printf("坐标非法,请重新输入\n");
		}
	}
}


//电脑下棋
void computer_move(char board[ROW][COL], int row, int col)
{
	int x = 0;
	int y = 0;
	printf("电脑下棋:>\n");

	while (1)
	{
		x = rand() % row;//0-2
		y = rand() % col;//0-2

		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;//跳出循环
		}
	}
}


//判断平局
static int is_full(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++)
		{
			if (board[i][j] == ' ')
			{
				return 0;//没满
			}
		}
	}

	return 1;//满了
}


//判断输赢
char is_win(char board[ROW][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][1]!= ' ')
		{
			return board[i][1];
		}
	}

	//判断列
	for (i = 0; i < col; i++)
	{
		if (board[0][i]==board[1][i] && board[1][i]==board[2][i] && board[1][i] != ' ')
		{
			return board[1][i];
		}
	}

	//判断对角线
	if (board[0][0]==board[1][1] && board[1][1]==board[2][2] && board[1][1] != ' ')
	{
		return board[1][1];
	}
	if (board[0][2]==board[1][1] && board[1][1]== board[2][0] && board[1][1] != ' ')
	{
		return board[1][1];
	}

	//判断平局
	if (is_full(board, row, col) == 1)
	{
		return 'Q';
	}

	//继续
	return 'C';
}

test.c

#define _CRT_SECURE_NO_WARNINGS 1

#include<stdio.h>
#include"game.h"


//test.c:测试三子棋游戏的逻辑
//game.c:游戏的实现
//game.h:函数声明,符号的定义


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


void game()
{
	char ret = 0;

	//存放下棋的数据
	char board[ROW][COL] = { 0 };

	//初始化棋盘为全空格
	InitBoard(board, ROW, COL);

	//打印棋盘
	DisplayBoard(board, ROW, COL);

	while (1)
	{
		//玩家下棋
		player_move(board, ROW, COL);
		DisplayBoard(board, ROW, COL);

		//判断输赢
		ret=is_win(board,ROW,COL);
		if (ret != 'C')
		{
			break;
		}

		//电脑下棋
		computer_move(board, ROW, COL);//随机下棋,哪里空着,往哪里下
		DisplayBoard(board, ROW, COL);

		//判断输赢
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
	}

	if (ret == '*')
	{
		printf("玩家赢了\n");
	}
	else if (ret == '#')
	{
		printf("电脑赢了\n");
	}
	else
	{
		printf("平局\n");
	}

	//打印棋盘
	DisplayBoard(board, ROW, COL);
}


//什么情况,游戏就结束了呢?
//玩家赢:'*'
//电脑赢:'#'
//平局:'Q'
//继续:'C'
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;
}

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

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

相关文章

帅地这些年看过的书

大家好&#xff0c;我是帅地。 好久没有给大家推荐书籍了&#xff0c;我一般很少给大家推荐书籍&#xff0c;因为自己没看过的&#xff0c;基本不推&#xff0c;只推荐我自己看过且自己自认为不错的书籍。 因为我自己本身是凭借着扎实的基础拿到大厂 offer 的&#xff0c;所以…

nodej+vues汽车销售4s店服务平台商城系统购物车积分兑换7z9d2

在经济快速发展的带动下&#xff0c;汽车服务平台的发展也是越来越快速。用户对汽车服务信息的获取需求很大。在互联网飞速发展的今天&#xff0c;制作一个汽车服务平台系统是非常必要的。本系统是借鉴其他人的开发基础上&#xff0c;用MySQL数据库和nodejs定制了汽车服务平台系…

Windows安装多个Mysql服务

1、正常安装好第一个 正常安装即可 2、第二个安装方法 1、官网下载zip包 MySQL :: MySQL Downloads 2、解压下载好的压缩包 &#xff08;注意修改文件夹名称&#xff0c;此时文件夹内并没有data文件夹&#xff09; 3、编写my.ini 注意修改端口号port以及安装目录basedir…

龙芯2K1000实战开发-平台介绍

文章目录 概要整体架构流程技术名词解释技术细节小结概要 龙芯 2K1000 处理器主要面向于网络应用,兼顾平板应用及工控领域应 用。采用 40nm 工艺,片内集成 2 个 GS264 处理器核,主频 1GHz,64 位 DDR3 控制器,以及各种系统 IO 接口。 整体架构 龙芯 2K1000 的结构如图 所…

《Oracle高级数据库》期末复习一文总结

文章目录 第一章&#xff1a;数据库基础1.数据库系统数据库数据库管理系统数据库系统 2.数据模型层次模型网状模型关系模型 3.关系型数据库&#xff08;1&#xff09;数据定义语言&#xff08;DDL&#xff09;&#xff08;2&#xff09;数据操纵语言&#xff08;DML&#xff09…

UC-OWOD: Unknown-Classified Open World Object Detection(论文翻译)

文章目录 UC-OWOD: Unknown-Classified Open World Object Detection摘要1.介绍2.相关工作3.未知分类的开放世界目标检测3.1 问题定义3.2 整体架构3.3 未知物体的检测3.4基于相似性的未知分类3.5未知聚类优化3.6训练和优化 4&#xff1a;实验4.1准备工作4.2结果和分析4.3消融研…

数学算法组合与排序

一句话总结&#xff1a;组合得次序是否重要&#xff0c;是否可重复&#xff0c;决定了组合数量 一、什么是组合&排序 组合可以是现实的一切事物、例如 [衣服&#xff0c;鞋子&#xff0c;眼镜...] 等等&#xff0c; 也可以表示一组数字 [1, 2, 3, 4, 5] &#xff0c;从个人…

STL常用容器_2

目录 一、stcak容器&#xff08;栈容器&#xff09; 1、基本概念 2、常用接口 二、queue容器&#xff08;队列容器&#xff09; 1、基本概念 2、常用接口函数 三、list容器&#xff08;链表&#xff09; 1、基本概念 2、构造函数 3、赋值与交换 4、大小操作 5、插入…

网络层和数据链路层

目录 网络层 IP协议 基本概念 协议头格式 ​编辑 网段划分 特殊的IP地址 IP地址的数量限制 私有IP地址和公网IP地址 路由 ​编辑数据链路层 以太网 以太网帧格式 认识MAC地址 对比理解MAC地址和IP地址 认识MTU MTU对IP协议的影响 ​编辑 MTU对UDP协议的影响 …

新产品上线前需要准备哪些产品文档呢

新产品上线前需要准备的产品文档非常重要&#xff0c;不仅有助于产品的开发过程中沟通和协作&#xff0c;而且对于后期的维护和升级也起到十分重要的作用。下面详细介绍新产品上线前需要准备哪些产品文档。 一、市场需求文档 市场需求文档&#xff08;Market Requirement Doc…

保姆级JAVA对接ChatGPT教程 使用 openai-gpt3-java

1. 前言 必须要有chatGTP 账号&#xff0c;如果需要测试账号可以关注公众号 疯狂的野猿 如果有chatGTP 账号就直接往下看。还需要一台外网服务器使用 nginx 代理来访问chatGTP 如果都没有&#xff0c;可以关注公众号联系作者。 还有笔者已经对接完成了&#xff0c;需要源码的关…

(电脑硬件)台式机主板音频端口功能详解

当你想给你的主机插上音响或者耳机时&#xff0c;你会发现主板上有6个接口&#xff0c;同样都是3.5mm接口&#xff0c;你知道该插哪个吗&#xff1f; 一般情况下&#xff0c;后置输入输出端口面板中&#xff0c;大多数的主板音频部分是彩色的。这一类颜色跟功能基本是固定的。当…

竟然支持在流程图、架构图中添加数学公式,安利一款纯免费的画图工具,真不错!

1. 简介 考虑到在绘图中需要添加数学表达式的场景&#xff0c;PDDON提供了LaTeX表达式编辑能力&#xff0c;可以在任何可以编辑的组件上启用LaTeX功能&#xff0c;使用LaTeX语法编写数学公式即可。 LaTeX表达式简介&#xff1a; LaTeX&#xff08;LATEX&#xff0c;音译“拉泰赫…

【偏门技巧】C语言编程实现对IPV4地址的合法性判断(使用正则表达式)

C语言编程实现对IPV4地址的合法性判断&#xff08;使用正则表达式&#xff09; 有了解过我的朋友&#xff0c;可能有点印象&#xff0c;我在N年前的博客中&#xff0c;就写了这个主题&#xff0c;当时确实是工作中遇到了这个问题。本想着等工作搞完之后&#xff0c;就把这个问题…

C++小知识点(for,nullptr)

&#x1f339;作者:云小逸 &#x1f4dd;个人主页:云小逸的主页 &#x1f4dd;Github:云小逸的Github &#x1f91f;motto:要敢于一个人默默的面对自己&#xff0c;强大自己才是核心。不要等到什么都没有了&#xff0c;才下定决心去做。种一颗树&#xff0c;最好的时间是十年前…

Ubuntu用户与用户组相关操作

目录 一、用户与用户组信息查看 二、用户管理 1、user1 2、user2 3、设置密码与删除用户 三、用户组管理 四、用户的切换 一、用户与用户组信息查看 查看用户&#xff0c;首先调出终端窗口&#xff0c;&#xff08;“sudo cat /etc/passwd”&#xff09;&#xff0c;输…

Day 50 小结

50.1 比较分析各种查找算法 顺序查找&#xff1a;时间复杂度&#xff1a;O(n)&#xff1b;可用于有序或无序数据&#xff1b;按顺序查找元素。 折半查找&#xff1a;时间复杂度&#xff1a;O(logn)&#xff1b;只能用于有序数据&#xff1b;从中间元素开始查找&#xff0c;每…

Linux 内核启动流程与入口函数分析

从启动引导程序 bootloader&#xff08;uboot&#xff09;跳转到 Linux 内核后&#xff0c;Linux 内核开始启动&#xff0c;今天我们分析一下 Linux 内核启动入口。 跳转过去初始化肯定是在汇编文件中&#xff0c;根据架构可以选择不同的平台&#xff0c;这里看一下链接汇编文…

STM32 Simulink 自动代码生成电机控制——记录一次电机初始位置检测及NS极的判断实验

目录 前言 基本原理 仿真实现 代码生成及开发板验证 前言 之前做了脉振高频注入的仿真到代码生成开发板运行的实验&#xff0c;电机可以通过高频注入计算出角度&#xff0c;但是在初始位置检测的时候&#xff0c;尝试了不少方法但是效果一般&#xff0c;很容易反转&#xff…

服务器模型 setsockopt 网络超时检测 广播组播和unix域套接字 5.23

四.服务器模型 1.循环服务器 TCP服务器 TCP服务器端运行后等待客户端的连接请求。 TCP服务器接受一个客户端的连接后开始处理&#xff0c;完成了客户的所有请求后断开连接。 TCP循环服务器一次只能处理一个客户端的请求。 只有在当前客户的所有请求都完成后&#xff0c;服务…