C语言经典小游戏之三子棋(超详解释+源码)

“纵有疾风来,人生不言弃,风乍起,合当奋意向此生。” 今天我们一起来学习一下三子棋小游戏用C语言怎么写出来?

三子棋小游戏

  • 1.游戏规则介绍
  • 2.游戏准备
  • 3.游戏的实现
    • 3.1生成菜单
    • 3.2游戏的具体实现
      • 3.2.1初始化棋盘
      • 3.2.2打印棋盘
      • 3.2.3玩家下棋(玩家下棋为O)
      • 3.2.4电脑下棋(电脑下棋为X)
      • 3.2.5判断输赢
      • 3.2.6棋盘的优化
  • 4.三子棋游戏完整的代码实现(详细源码)

1.游戏规则介绍

《三子棋》是一款古老的民间传统游戏,又被称为黑白棋、圈圈叉叉棋、井字棋、一条龙、九宫棋等。游戏分为双方对战,双方依次在9宫格棋盘上摆放棋子,率先将自己的三个棋子连成一条线的一方则视为胜利者。
在这里插入图片描述

2.游戏准备

这里呢,我们需要准备三个源文件,分别是test.c game.c game.h(名称随意,尽量通俗易通一些比较好)可能有疑问了,这里为什么需要三个源文件呢?会不会显得很麻烦?在前面的学习中,我们也提到了未来在工程中代码比较多,函数一般放在.h文件中声明,在.c文件中实现。
分文件书写形式的优点:1.方便多人协作 2.保护代码

这里我们给大家介绍一下在三子棋这个游戏里面,每个文件是做什么的呢?
(1)头文件game.h,头文件里是用来存放函数的声明,#define常量的定义,库函数的引用的。
(2)源文件test.c,这个文件里面放的是游戏的测试逻辑。
(3)源文件game.c,这个文件里面放的是游戏的实现逻辑(函数实现)。

3.游戏的实现

3.1生成菜单

这里呢,我们通过switch语句给用户选择,当用户输入不同的数字,我们的程序就会给出不同的功能。

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
void menu()//生成菜单的函数
{
	printf("******************************\n");
	printf("**********  1.play  **********\n");
	printf("**********  0.exit  **********\n");
	printf("******************************\n");
}
void game()//实现玩游戏的函数
{

}
int main()
{
	int input = 0;//用户选择
	do//利用循环可以便于用户进行多次游戏
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏!\n");
			break;
		default:
			printf("选择错误,重新选择!\n");
			break;
		}
	} while (input);
	return 0;
}

通过上面的代码,我们已经构建出了基本的框架:
在这里插入图片描述

3.2游戏的具体实现

我们发现,在上面的框架代码中缺少了游戏实现部分的代码,这里,我们在慢慢的一点点细化游戏过程。
在这里插入图片描述

首先,我们可以看到,我们需要先构造出一个3*3的棋盘。使得每个九个空位都处于空的状态。这里我们就想到了二维数组的概念,那我们一起来试试吧!

3.2.1初始化棋盘

//game.h
#pragma once
# define ROW 3
# define COL 3
//初始化棋盘的函数的声明
void InitBoard(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;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		for (j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}

//test.c
void game()
{
	//最开始的时候数组的内容全是空格
	char board[ROW][COL]; 
	InitBoard(board, ROW, COL);
}

在这里插入图片描述

3.2.2打印棋盘

初始化棋盘之后,我们需要将棋盘打印出来才能被用户看到,那么我们同样的可以利用for循环来完成这步操作。

//game.h
//打印棋盘的函数声明
void DisplayBoard(char board[ROW][COL], int row, int col);

//game.c
void DisplayBoard(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++)
		{
			printf("%c", board[i][j]);
		}
		printf("\n");
	}
}

//test.c
DisplayBoard(board, ROW, COL);

在这里插入图片描述
在这里插入图片描述
但是我们发现,这里留下了一大片空白,根本看不到棋盘,这是为什么呢?因为我们已经将棋盘初始化为空格了,显然我们是看不到的,而我们想要的是九宫格的样子,这里的话我们就可以用-和|来组成我们的棋盘了。

//game.h
//打印棋盘的函数声明
void DisplayBoard(char board[ROW][COL], int row, int col);

//game.c
void DisplayBoard(char board[ROW][COL], int row, int col)//打印棋盘函数的实现
{
	int i = 0;
	int j = 0;
	for (i = 0; i < row; i++)
	{
		printf(" %c | %c | %c \n", board[i][0], board[i][1],board[i][2]);
		printf("---|---|---\n");
	}
}

//test.c
DisplayBoard(board, ROW, COL);

但是,我们发现,这个棋盘还是存在不足,它的下面多了一条线,那么我们就需要调整一下for循环的循环语句了。
在这里插入图片描述
这里,我们给出修改后的代码及运行结果:

//game.h
//打印棋盘的函数声明
void DisplayBoard(char board[ROW][COL], int row, int col);

//game.c
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)
		{
			int j = 0;
			for (j = 0; j < col; j++)
			{
				printf("---");//再打印分隔行
				if (j < col - 1)
				{
					printf("|");
				}
			}	
		}
		printf("\n");
	}
}

//test.c
DisplayBoard(board, ROW, COL);

在这里插入图片描述
这样的话,我们通过修改# define ROW 3 # define COL 3的值来改变棋盘的大小:
在这里插入图片描述

3.2.3玩家下棋(玩家下棋为O)

//game.h
//玩家下棋函数声明
void PlayerMove(char board[ROW][COL], int row, int col);

//game.c
//玩家下棋函数的实现
void PlayerMove(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] == ' ')//玩家并不会考虑到数组下标从0开始,这里我们需要考虑进去
			{
				board[x - 1][y - 1] = 'O';
				break;
			}
			else
			{
				printf("该坐标已被占用,请重新下棋!\n");
			}
		}
		else
		{
			printf("坐标非法,请重新输入!\n");
		}
	}
}

//test.c
while (1)
	{
		PlayerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);//将玩家下棋结果打印出来
	}

在这里插入图片描述

3.2.4电脑下棋(电脑下棋为X)

//game.h
//电脑下棋函数声明
void ComputerMove(char board[ROW][COL], int row, int col);

//game.c
//电脑下棋函数的实现
//电脑随机下棋
void ComputerMove(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋\n");
	int x = 0;
	int y = 0;
	while (1)
	{
		x = rand() % row;//电脑随机产生坐标
		y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = 'X';
			break;
		}
	}
}

//test.c
while (1)
	{
		ComputerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);//将玩家下棋结果打印出来
	}

在这里插入图片描述
但是,我们这里目前只考虑让这个游戏玩起来,电脑下棋只是随机产生一些值,并没有任何的逻辑在里面。

3.2.5判断输赢

输赢情况有以下几种:
1.玩家赢-返回O
2.电脑赢-返回X
3.平局-返回Q
4.游戏继续-返回C

//game.h
//判断输赢函数声明
char Is_win(char board[ROW][COL], int row, int col);

//game.c
//判断棋盘是不是满了的函数
static int Is_full(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] == ' ')
				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][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[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))
	{
		return 'Q';
	}
	return 'C';
}

//test.c
if (ret == 'O')
	{
		printf("玩家赢!\n");
	}
		
	else if (ret == 'X')
	{
		printf("电脑赢!\n");
	}
	else
	{
		printf("平局!\n");
	}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.2.6棋盘的优化

我们发现程序基本已经可以运行了,但是如果添加清屏的操作会不会更好呢?这里,我们可以完善一下。
这里我们就需要引入system("cls"),它就可以实现清屏的操作啦!
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

4.三子棋游戏完整的代码实现(详细源码)

game.h文件

#pragma once
#include <stdio.h>
#include <time.h>
#include <stdlib.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 PlayerMove(char board[ROW][COL], int row, int col);
//电脑下棋函数声明
void ComputerMove(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;
	int 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;
	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[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] = 'O';
				break;
			}
			else
			{
				printf("该坐标已被占用,请重新下棋!\n");
			}
		}
		else
		{
			printf("坐标非法,请重新输入!\n");
		}
	}
}
//电脑下棋函数的实现
//电脑随机下棋
void ComputerMove(char board[ROW][COL], int row, int col)
{
	printf("电脑下棋\n");
	int x = 0;
	int y = 0;
	while (1)
	{
		x = rand() % row;//电脑随机产生坐标
		y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = 'X';
			break;
		}
	}
}
//判断棋盘是不是满了的函数
static int Is_full(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] == ' ')
				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][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[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))
	{
		return 'Q';
	}
	return 'C';
}

test.c文件

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include "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]; 
	InitBoard(board, ROW, COL);
	DisplayBoard(board, ROW, COL);
	while (1)
	{
		PlayerMove(board, ROW, COL);//玩家下棋函数调用
		DisplayBoard(board, ROW, COL);//将玩家下棋结果打印出来
		//判断输赢
		ret = Is_win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}
		system("cls");//清屏
		DisplayBoard(board, ROW, COL);
		ComputerMove(board, ROW, COL);//电脑下棋函数调用
		DisplayBoard(board, ROW, COL);//将电脑下棋结果打印出来
		ret = Is_win(board, ROW, COL);
		if (ret != 'C')
		{
			break;
		}	
		system("cls");//清屏
		DisplayBoard(board, ROW, COL);
	}
	system("cls");//清屏
	DisplayBoard(board, ROW, COL);
	if (ret == 'O')
	{
		printf("玩家赢!\n");
	}
		
	else if (ret == 'X')
	{
		printf("电脑赢!\n");
	}
	else
	{
		printf("平局!\n");
	}
		
}
int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏!\n");
			break;
		default:
			printf("选择错误,重新选择!\n");
			break;
		}
	} while (input);
	return 0;
 }

以上就是关于三子棋游戏的全部代码啦!当然这个程序还是存在可优化的空间(比如电脑下棋的时候能不能更加智能一点,还有待思考),欢迎大家在评论区交流。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

TypeScript【enum 枚举】

导语 在 TypeScript 中&#xff0c;新增了很多具有特性的一些数据类型处理方法&#xff0c;enum 【枚举】就是其中&#xff0c;很具有代表性的一种&#xff0c;所以本章节就来聊聊 在 TypeScript 中如何去运用 enum 【枚举】。 枚举的概念&#xff1a; 枚举&#xff08;Enum&am…

机器学习基础08-模型选择02-分类算法矩阵(基于Pima 数据集)

算法评估矩阵&#xff08;Algorithm Evaluation Metrics&#xff09;用于评估机器学习算法在特定任务上的性能。不同的任务可能会使用不同的评估矩阵&#xff0c;因为每个任务的优劣衡量标准都不同。 分类算法矩阵 分类问题或许是最常见的机器学习问题&#xff0c;并且有多种评…

【算法心得】C++map用不着map.find(arr[j])!=map.end();js的map是map不是哈希;编译器选GNU

https://leetcode.com/problems/count-of-range-sum/ https://vjudge.csgrandeur.cn/problem/CodeForces-459D 这两题都是线段树&#xff0c;很明显的单点修改区间查询 leetcode那题我觉得map用hashmap就行&#xff0c;但是好像js里没有hashmap&#xff0c;那就 Map() 也行吧…

vue、uniapp直传阿里云文档

前端实现文件上传到oss&#xff08;阿里云&#xff09;适用于vue、react、uni-app&#xff0c;获取视频第一帧图片 用户获取oss配置信息将文件上传到阿里云&#xff0c;保证了安全性和减轻服务器负担。一般文件资源很多直接上传到服务器会加重服务器负担此时可以选择上传到oss&…

SpringCloudAlibaba之Sentinel(一)流控篇

前言&#xff1a; 为什么使用Sentinel&#xff0c;这是一个高可用组件&#xff0c;为了使我们的微服务高可用而生 我们的服务会因为什么被打垮&#xff1f; 一&#xff0c;流量激增 缓存未预热&#xff0c;线程池被占满 &#xff0c;无法响应 二&#xff0c;被其他服务拖…

阿里云容器服务助力极氪荣获 FinOps 先锋实践者

作者&#xff1a;海迩 可信云评估是中国信息通信研究院下属的云计算服务和软件的专业评估体系&#xff0c;自 2013 年起历经十年发展&#xff0c;可信云服务评估体系已日臻成熟&#xff0c;成为政府支撑、行业规范、用户选型的重要参考。 2022 年 5 月国务院国资委制定印发《…

Linux 快速创建桌面图标

在安装 tar.gz 这类型压缩文件时&#xff0c;通常启动文件是.sh文件。文章主要记录快速添加到桌面图标。 1、解压 tar -zxvf XXX.tar.gz 2、创建桌面图标文件 touch XXX.desktop 3、文件中配置 [Desktop Entry] NameXXX CommentZZZ Exec/软件可执行文件所在目录/可执行文…

SpringBoot核心内容梳理

1.SpringBoot是什么? Spring Boot是一个基于Spring框架的快速开发应用程序的工具。它简化了Spring应用程序的创建和开发过程&#xff0c;使开发人员能够更快速地创建独立的、生产就绪的Spring应用程序。它采用了“约定优于配置”的原则&#xff0c;尽可能地减少开发人员需要进…

为代码生成一个良好可读的API文档-Doxygen简单实战

需求&#xff1f;为什么要有API文档 在代码开发过程中&#xff0c;我们会发现有这样的情况&#xff0c;其他团队的代码和自己团队的代码相异甚大&#xff0c;如果没有一个统一规范的文档来对接&#xff0c;会造成很多交流沟通上的不便&#xff0c;但我们又不想浪费时间去边写说…

C++STL——map/multimap容器详解

纵有疾风起&#xff0c;人生不言弃。本文篇幅较长&#xff0c;如有错误请不吝赐教&#xff0c;感谢支持。 &#x1f4ac;文章目录 一.对组&#xff08;pair&#xff09;二.map/multimap基本概念三.map容器常用操作①map构造函数②map迭代器获取③map赋值操作④map大小操作⑤map…

转录组下游分析 | 懒人分析推荐

写在前面 今天在GitHub看到一个博主写的RNASeqTool的ShinApp&#xff0c;里面包含了PCA、DESeq2、volcano、NormEnrich、GSEA、Gene tred analysis和WGCNA分析。使用后还是很方便的&#xff0c;就此推荐给大家。感兴趣可以自己操作即可。 GitHub网址 https://github.com/Cha…

C++、python双语言弹窗教程与对比

Messagebox弹窗 MessageBox指的是显示一个模态对话框&#xff0c;其中包含一个系统图标、 一组按钮和一个简短的特定于应用程序消息&#xff0c;如状态或错误的信息。消息框中返回一个整数值&#xff0c;该值指示用户单击了哪个按钮。 例子&#xff1a; 本文介绍了用C、Pytho…

flask服务生成证书文件,采用https访问,开启用户密码验证

openssl req -x509 -newkey rsa:4096 -nodes -out cert.pem -keyout key.pem -days 3072开启用户密码验证 auth.verify_password def verify_password(username, password):if username abcdefg and password 1234546:return usernameapp.route(/post_request, methods[POST…

使用Canvas裁剪图片

使用Canvas裁剪图片 概述 在Web开发中&#xff0c;我们经常需要对图片进行裁剪&#xff0c;以满足不同尺寸需求或者实现图片的局部展示。本篇博客将带您深入了解如何使用Canvas技术来实现图片的裁剪功能。我们将通过一个实例来演示如何利用Canvas绘制图片&#xff0c;并通过蒙…

【LeetCode】647. 回文子串

题目链接 文章目录 1. 思路讲解1.1 方法选择1.2 dp表的创建1.3 状态转移方程1.4 填表顺序 2. 代码实现 1. 思路讲解 1.1 方法选择 这道题我们采用动态规划的解法&#xff0c;倒不是动态规划的解法对于这道题有多好&#xff0c;它并不是最优解。但是&#xff0c;这道题的动态…

devops-发布vue前端项目

回到目录 将使用jenkinsk8s发布前端项目 1 环境准备 node环境 在部署jenkins的服务器上搭建node环境 node版本 # 1.拉取 https://nodejs.org/download/release/v20.4.0/node-v20.4.0-linux-x64.tar.gz# 2.解压到/usr/local目录下 sudo tar xf v20.4.0.tar.gz -C /usr/loc…

正则表达式的应用及示例解析

正则表达式&#xff08;Regular Expression&#xff0c;简称Regex&#xff09;是由特殊字符组成的模式字符串&#xff0c;用于匹配和搜索文本中的特定模式。它在数据处理、文本搜索和替换等方面广泛应用。本文将介绍正则表达式的基本语法&#xff0c;并提供常见的正则表达式示例…

关于数据权限的设计

在项目实际开发中我们不光要控制一个用户能访问哪些资源&#xff0c;还需要控制用户只能访问资源中的某部分数据。 控制一个用户能访问哪些资源我们有很成熟的权限管理模型即RBAC&#xff0c;但是控制用户只能访问某部分资源&#xff08;即我们常说的数据权限&#xff09;使用R…

iTOP-RK3588开发板Ubuntu 系统交叉编译 Qt 工程-命令行交叉编译

使用源码 rk3588_linux/buildroot/output/rockchip_rk3588/host/bin/qmake 交叉编译 QT 工程。 最后烧写编译好的 buildroot 镜像&#xff0c;将编译好的 QT 工程可执行程序在 buildroot 系统上运行。 交叉编译 QT 工程如下所示&#xff0c;首先进入 QLed 的工程目录下。 然后…

什么样的跨网文件安全交换系统 可实现安全便捷的文件摆渡?

进入互联网时代&#xff0c;网络的运算和数据管理能力助力各个行业高速发展&#xff0c;但同样带来了一些网络安全隐患&#xff0c;网络攻击、数据窃取、敏感信息泄露等问题。为此&#xff0c;我国出台了系列政策来全面提升银各行业系统网络安全整体防护水平&#xff0c;其中“…