C语言之三子棋游戏(附完整代码)

news2024/11/22 21:25:43

学了那么多关于C语言的知识,也该进行一下实操了。三子棋游戏应该是大家学生时代课间比较喜欢娱乐消遣的一种方式吧。那么我们今天就来说说如何实现简单版本的三子棋对战小游戏吧。

三子棋游戏介绍

        三子棋游戏类似于五子棋,不同的是它的棋盘大小是九宫格且达到如下图一种结果则算赢。三子棋游戏的规则是必须 横向 或 纵向 或 斜线 填满相同的棋子才算赢。

三子棋游戏实现的编程框架

        1. 游戏不退出,当结束一局可以进行下一局(循环)。
        2. 应用多文件的形式完成代码。在企业团队进行项目开发时,多文件形式写代码是必不可少的,因此这里我们也用多文件形式写代码,这些文件都必须放在同一个工程(即同一个文件夹)下。
                 test.c         --       测试游戏的,即主要是main函数内对游戏功能进行框架搭建和测试。
                game.c       --       游戏函数的实现,即主要是完成游戏核心功能的函数的具体实现。
                game.h       --       游戏函数的声明,即主要是对这些函数的声明和简介。

不同文件的实现

(一)game.h文件

该文件为实现三子棋项目的声明。

#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 3
#define COL 3

void InitGame(char board[ROW][COL], int row, int col);//初始化棋盘
void DisPlayBoard(char board[ROW][COL], int row, int col);//显示棋盘
void PlayerGame(char board[ROW][COL], int row, int col);//玩家下棋
void ComputerGame(char board[ROW][COL], int row, int col);//电脑下棋

//玩家赢返回‘*’
//电脑赢返回‘#’
//平局返回‘Q’
//可以继续下棋返回‘C’
char is_win(char board[ROW][COL], int row, int col);//判断输赢
int if_full(char board[ROW][COL], int row, int col);//判断棋盘是否下满

宏定义的变量ROW 和COL表示棋盘的大小是三行三列的九宫格,在game.h中声明棋盘大小。如果想改变棋盘的大小,直接在该文件中进行修改即可。

(二)test.c文件

1.菜单功能

实现一个简易的菜单功能,提醒玩家选择开始游戏还是结束游戏。选择1表示开始游戏,选择0表示结束游戏。

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

2.main函数实现连续多玩

由于我们游戏开发的目标是实现玩家可以选择连续多玩游戏,因此本段代码主要的逻辑是使用循环控制。而且我们程序运行时必须至少进行一次游戏,所以采取do……while循环进行控制,具体代码逻辑可以参考之前写的博客C语言之猜数字小游戏。

int main()
{
	int input = 0;
	do {
		menu();
		scanf("%d", &input);
		switch (input)
		{
		default:
			printf("出错啦,公主请重新输入:\n");
			break;
		case 1:
			printf("尊敬的公主,请开始你的游戏吧!\n");
			game();
			break;
		case 0:
			printf("游戏退出,公主拜拜!\n");
			break;
		}
	} while (input);
	return 0;
}

3.game函数的实现

game函数是实现游戏流程,如打印棋盘,玩家下棋,电脑下棋,并判断输赢等。当然game函数中只是对这些函数的调用,核心功能的函数实现放在game.c源文件中。代码如下:

void game()
{
	char board[ROW][COL] = { 0 };
	srand((unsigned int)time(NULL));//设置随机种子,实现每次落子随机
	char ret = 0;//对决输赢的返回值
	InitGame(board, ROW, COL);//初始化棋盘
	DisPlayBoard(board, ROW, COL);
	while (1)
	{
		PlayerGame(board, ROW, COL);//玩家下棋
		DisPlayBoard(board, ROW, COL);//显示对决情况
		ret = is_win(board, ROW, COL);//判断输赢
		if (ret != 'C')
			break;
		ComputerGame(board, ROW, COL);//电脑下棋
		DisPlayBoard(board, ROW, COL);//显示对决情况
		ret = is_win(board, ROW, COL);//判断输赢
		if (ret != 'C')
			break;
	}
	if (ret == 'Q')
	{
		printf("达成平局啦!\n");
	}
	else if (ret == '*')
	{
		printf("恭喜公主获胜啦!\n");
	}
	else
	{
		printf("公主还要加油啦,电脑获胜咯!\n");
	}

}

本代码使用'“*”表示玩家棋子,“#”表示电脑棋子,使用二维数组存储棋子的位置,而此二维数组相当于棋盘。最开始时要初始化棋盘并打印棋盘,以让玩家值观看见棋盘并准备下棋。下棋时不是只下一颗棋就结束了,所以要用循环,直至分出结果后结束循环,判断游戏结局。同时,玩家和电脑每下一颗棋时都要打印棋盘并判断是否分出胜负,如果没有分出胜负则继续下棋(不跳出循环)。

(三)game.c文件

该文件是实现整个游戏最核心的逻辑部分。

1.初始化棋盘(InitGame)

        游戏实现是使用字符作为玩家和电脑的棋子,在初始化时,用空格来初始化棋盘(空格也是一个字符)。二维数组用两层for循环对数组元素进行赋值即可。

void InitGame(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}

2.显示棋盘对决情况(DisPlayBoard)


//版本1 不推荐
//该版本在修改棋盘大小时,printf函数中的内容也要进行相应的修改,增加了后续维护的工作量
//void DisplayBoard(char board[ROW][COL], int row, int col) {
//	int i = 0;
//	for (i = 0; i < row; i++) {
//		// 1. 打印数据
//		printf(" %c | %c | %c \n", board[i][0], board[i][1], board[i][2]);
//		// 2. 打印分割线
//		if (i < row - 1) {
//			printf("---|---|---\n");
//		}	
//	}
//}


//版本2 推荐版本,针对棋盘大小发生变化时可以动态修改
void DisPlayBoard(char board[ROW][COL], int row, int col)
{
//1 打印数据
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1) {
				printf("|");
			}
		}
		printf("\n");
//2 打印分割线
		for (int j = 0; j < col; j++)
		{
				printf("___");
			if (j < col - 1) 
			{
				printf("|");
			}
		}
		printf("\n");
	}
	printf("\n");
}

棋盘设计情况如下图

3.玩家下棋 (PlayerGame)

        在玩家下棋时,需要考虑玩家输入的坐标是否合法问题,如果玩家输入坐标越界或者玩家输入的坐标已经有棋子时都要给出相应的提醒,并让玩家重新输入坐标直至输入正确。因此使用循环实现整个代码逻辑。
        当然,在我们玩家眼中坐标都是1,2,3,而数组的下标是从0开始的,所以当玩家输入坐标后需要进行减1。

void PlayerGame(char board[ROW][COL], int row, int col)
{
	printf("公主请落子,输入落子的坐标:");
	int x = 0;
	int y = 0;
	scanf("%d %d", &x, &y);
	while (1)
	{
		if (x >= 1 && x <= row && y >= 1 && y <= col && board[x - 1][y - 1] == ' ')
		{
			board[x - 1][y - 1] = '*';
			break;
		}
		else
		{
			printf("尊敬的公主,输入的坐标不对嗷,请重新输入:");
			scanf("%d %d", &x, &y);
		}
	}
}

4.电脑下棋(ComputerGame)

void ComputerGame(char board[ROW][COL], int row, int col)
{
	printf("下面是电脑落子!\n");
	int x = 0, y = 0;
	while (1)
	{
		x = rand() % row;
		y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

        这里的row和col都是3,所以rand()%row,rand()%col已经满足了电脑随机产生的坐标不会越界,取余产生的数字也是0、1、2,刚好与数组下标相符,而当遇到空格子时电脑才完成下棋,因此电脑下棋并不会覆盖原来已经下好的棋子。

5.判断棋盘是否下满(if_full)

int if_full(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')
				return 0;
		}
	}
	return 1;
}

若还有一个空位,则说明棋盘没有下满,此时返回0;反之,返回1,即棋盘已经下满。

6.判断输赢(is_win)

        玩游戏,不仅要享受玩的过程,还要注重游戏的结果。在本篇文章开头已经介绍了三子棋游戏胜利的8种情况,因此在函数编写时则按照行、列、斜线依次进行结果的判定。

        设定该函数返回的结果对应的下棋对决结果如下:

  1. 玩家赢                                       ------              ‘*’
  2. 电脑赢                                       ------              ‘#’
  3. 平局                                          ------              ‘Q’
  4. 可以继续下棋                            ------              ‘C’
//玩家赢返回‘*’
//电脑赢返回‘#’
//平局返回‘Q’
//可以继续下棋返回‘C’
char is_win(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		int row_flag = 1;//判断行是否相同,假设是相同
		for (int j = 1; j < col; j++)
		{
			if (board[i][j] != board[i][0])
			{
				row_flag = 0;//有一个不同,则行不同
				break;
			}
		}
		if (row_flag == 1 && board[i][0] != ' ') return board[i][0];
	}

	for (int i = 0; i < col; i++)
	{
		int col_flag = 1;//判断列是否相同,假设是相同
		for (int j = 1; j < row; j++)
		{
			if (board[j][i] != board[0][i])
			{
				col_flag = 0;//有一个不同,则列不同
				break;
			}
		}
		if (col_flag == 1 && board[0][i] != ' ') return board[0][i];
	}

	//判断正对角线是否相同
	int zflag = 1;
	for (int i = 1; i < row; i++)
	{
		if (board[i][i] != board[0][0]) {
			zflag = 0;
			break;
		}
	}
	if (zflag == 1 && board[0][0] != ' ')	return board[0][0];

	//判断反对角线是否相同
	int fflag = 1;
	for (int i = 0; i < row; i++)
	{
		if (board[0][COL - 1] != board[i][COL - 1 - i])
		{
			fflag = 0;
			break;
		}
	}
	if (fflag == 1 && board[0][COL - 1] != ' ')	return board[0][COL - 1];

	//棋盘下满返回平局
	if (if_full(board,ROW,COL) == 1)	return 'Q';

	//棋盘没下满、玩家或者电脑没有一方获胜,则可以继续下棋。
	return 'C';
}

完整代码

game.h

#pragma once

#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define ROW 3
#define COL 3

void InitGame(char board[ROW][COL], int row, int col);
void DisPlayBoard(char board[ROW][COL], int row, int col);
void PlayerGame(char board[ROW][COL], int row, int col);
void ComputerGame(char board[ROW][COL], int row, int col);
char is_win(char board[ROW][COL], int row, int col);
int if_full(char board[ROW][COL], int row, int col);

game.c

#define _CRT_SECURE_NO_WARNINGS 1

#include "game.h"

void InitGame(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			board[i][j] = ' ';
		}
	}
}

void DisPlayBoard(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			if (j < col - 1) {
				printf("|");
			}
		}
		printf("\n");
		for (int j = 0; j < col; j++)
		{
				printf("___");
			if (j < col - 1) 
			{
				printf("|");
			}
		}
		printf("\n");
	}
	printf("\n");
}

void PlayerGame(char board[ROW][COL], int row, int col)
{
	printf("公主请落子,输入落子的坐标:");
	int x = 0;
	int y = 0;
	scanf("%d %d", &x, &y);
	while (1)
	{
		if (x >= 1 && x <= row && y >= 1 && y <= col && board[x - 1][y - 1] == ' ')
		{
			board[x - 1][y - 1] = '*';
			break;
		}
		else
		{
			printf("尊敬的公主,输入的坐标不对嗷,请重新输入:");
			scanf("%d %d", &x, &y);
		}
	}
}

void ComputerGame(char board[ROW][COL], int row, int col)
{
	printf("下面是电脑落子!\n");
	int x = 0, y = 0;
	while (1)
	{
		x = rand() % row;
		y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}


int if_full(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		for (int j = 0; j < col; j++)
		{
			if (board[i][j] == ' ')
				return 0;
		}
	}
	return 1;
}

//玩家赢返回‘*’
//电脑赢返回‘#’
//平局返回‘Q’
//可以继续下棋返回‘C’
char is_win(char board[ROW][COL], int row, int col)
{
	for (int i = 0; i < row; i++)
	{
		int row_flag = 1;//判断行是否相同,假设是相同
		for (int j = 1; j < col; j++)
		{
			if (board[i][j] != board[i][0])
			{
				row_flag = 0;//有一个不同,则行不同
				break;
			}
		}
		if (row_flag == 1 && board[i][0] != ' ') return board[i][0];
	}

	for (int i = 0; i < col; i++)
	{
		int col_flag = 1;//判断列是否相同,假设是相同
		for (int j = 1; j < row; j++)
		{
			if (board[j][i] != board[0][i])
			{
				col_flag = 0;//有一个不同,则列不同
				break;
			}
		}
		if (col_flag == 1 && board[0][i] != ' ') return board[0][i];
	}

	//判断正对角线是否相同
	int zflag = 1;
	for (int i = 1; i < row; i++)
	{
		if (board[i][i] != board[0][0]) {
			zflag = 0;
			break;
		}
	}
	if (zflag == 1 && board[0][0] != ' ')	return board[0][0];

	//判断反对角线是否相同
	int fflag = 1;
	for (int i = 0; i < row; i++)
	{
		if (board[0][COL - 1] != board[i][COL - 1 - i])
		{
			fflag = 0;
			break;
		}
	}
	if (fflag == 1 && board[0][COL - 1] != ' ')	return board[0][COL - 1];

	//棋盘下满返回平局
	if (if_full(board,ROW,COL) == 1)	return 'Q';

	//棋盘没下满、玩家或者电脑没有一方获胜,则可以继续下棋。
	return 'C';
}

test.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 game()
{
	char board[ROW][COL] = { 0 };
	srand((unsigned int)time(NULL));
	char ret = 0;
	InitGame(board, ROW, COL);
	DisPlayBoard(board, ROW, COL);
	while (1)
	{
		PlayerGame(board, ROW, COL);
		DisPlayBoard(board, ROW, COL);
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
			break;
		ComputerGame(board, ROW, COL);
		DisPlayBoard(board, ROW, COL);
		ret = is_win(board, ROW, COL);
		if (ret != 'C')
			break;
	}
	if (ret == 'Q')
	{
		printf("达成平局啦!\n");
	}
	else if (ret == '*')
	{
		printf("恭喜公主获胜啦!\n");
	}
	else
	{
		printf("公主还要加油啦,电脑获胜咯\n");
	}

}

int main()
{
	int input = 0;
	do {
		menu();
		scanf("%d", &input);
		switch (input)
		{
		default:
			printf("出错啦,公主请重新输入:\n");
			break;
		case 1:
			printf("尊敬的公主,请开始你的游戏吧!\n");
			game();
			break;
		case 0:
			printf("游戏退出,公主拜拜!\n");
			break;
		}
	} while (input);
	return 0;
}

程序运行效果

希望这篇博客能对大家有一定的帮助,激发对C语言学习的兴趣。预告一下,下一篇将带大家实现扫雷小游戏!敬请期待吧!

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

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

相关文章

LLM | Ollama 安装、运行大模型(CPU 实操版)

1. 操作步骤 1.1 安装 # 通过 homebrew 安装 brew install ollama1.2 验证&#xff08;可跳过&#xff09; # 输出命令使用提示则安装成功 ollama --help1.3 启动服务端 # 启动 ollama 服务&#xff08;默认在 11434 端口&#xff0c;模型文件在 ~/.ollama&#xff09; oll…

论文阅读笔记-Pre-trained Models for Natural Language Processing: A Survey

前言 预训练模型给下游任务带来的效果不言而喻,有了预训练模型,我们可以使用它来加速解决问题的过程。正如论文中所说的那样,预训练模型(PTMs)的出现将自然语言处理(NLP)带入了一个新时代。本篇论文基于分类从四个角度对现有PTMs进行系统分类,描述如何使PTMs的知识适应…

Spring源码学习:SpringMVC(4)DispatcherServlet请求入口分析

目录 前言HttpServlet &FrameworkServletHttpServlet #serviceFrameworkServlet#processRequest DispatcherServlet#doServicedoDispatchcheckMultipartgetHandlerAbstractHandlerMapping#getHandlerRequestMappingInfoHandlerMapping#getHandlerInternalAbstractHandlerMe…

Excel-查找和引用数据-VLOOKUP 和 HLOOKUP 函数

在 Excel 中&#xff0c;VLOOKUP 和 HLOOKUP 是用于查找和引用数据的函数。下面是它们的基本用法&#xff1a; VLOOKUP 用途&#xff1a;在表格的第一列中查找某个值&#xff0c;并返回该值所在行的指定列中的数据。 语法&#xff1a; VLOOKUP(lookup_value, table_array, …

多模态大语言模型(MLLM)-Blip2深度解读

前言 Blip2是一个多模态大语言模型&#xff0c;因其提出时间较早&#xff08;2023年&#xff09;&#xff0c;且效果较好&#xff0c;很快成为一个标杆性工作。Blip2中提出的Q-former也成为衔接多模态和文本的重要桥梁。 Blip2发表时间是2023年&#xff0c;现在引用已经3288了…

产品经理内容分享(一):AI产品经理需必备那些能力

目录 必备的AI技术知识 第一章&#xff1a;AI产品经理是否需要懂技术及其程度 第二章&#xff1a;AI产品经理必备的AI技术基础知识——基础算法与机器学习方法 第三章&#xff1a;AI产品经理必须要懂的AI技术知识——场景应用 第四章&#xff1a;AI算法与模型的关系 第五…

PhotoMaker部署文档

一、介绍 PhotoMaker&#xff1a;一种高效的、个性化的文本转图像生成方法&#xff0c;能通过堆叠 ID 嵌入自定义逼真的人类照片。相当于把一张人的照片特征提取出来&#xff0c;然后可以生成你想要的不同风格照片&#xff0c;如写真等等。 主要特点&#xff1a; 在几秒钟内…

求1000以内的完数

题目&#xff1a;一个数如果恰好等于他的因子之和&#xff08;包括1&#xff0c;但不包括这个数&#xff09;&#xff0c;这个数就是完数。编写算法找出1000之内的所有完数&#xff0c;并按下面格式输出其因子&#xff1a;28 its factors are 1,2,4,7,14 代码如下&#xff1a;…

Dell服务器电源配置

Dell服务器电源配置规则 PowerEdge 电源设置

医院综合服务系统小程序的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;患者管理&#xff0c;医生管理&#xff0c;就诊信息管理&#xff0c;科室信息管理&#xff0c;挂号信息管理&#xff0c;系统管理 微信端账号功能包括&#xff1a;系统首页&#xff0c;订单排队&#…

《PyTorch深度学习快速入门教程》学习笔记(第15周)

目录 摘要 Abstract 1. 安装Anaconda 2. 查看显卡驱动 3. 安装Pytorch 4. Pytorch加载数据 5. 常用数据集两种形式 6. 路径直接加载数据 7. Dataset加载数据 摘要 本周报的目的在于汇报《PyTorch深度学习快速入门教程》课程第一周的学习成果&#xff0c;主要聚焦于py…

微服务Sleuth解析部署使用全流程

目录 1、Sleuth链路追踪 1、添加依赖 2、修改日志配置文件 3、测试 2、zipkin可视化界面 1、docker安装 2、添加依赖 3、修改配置文件 4、查看页面 5、ribbon配置 1、Sleuth链路追踪 sleuth是链路追踪框架&#xff0c;用于在微服务架构下开发&#xff0c;各个微服务之…

轻松部署大模型:Titan Takeoff入门指南

轻松部署大模型&#xff1a;Titan Takeoff入门指南 在人工智能的快速发展中&#xff0c;处理自然语言处理&#xff08;NLP&#xff09;任务的大规模语言模型&#xff08;LLM&#xff09;至关重要。然而&#xff0c;部署这些模型往往具有挑战性&#xff0c;需要高性能的硬件和优…

设计模式之适配器模式(Adapter)

一、适配器模式介绍 适配器模式(adapter pattern )的原始定义是&#xff1a;将类的接口转换为客户期望的另一个接口&#xff0c; 适配器可以让不兼容的两个类一起协同工作。 适配器模式是用来做适配&#xff0c;它将不兼容的接口转换为可兼容的接口&#xff0c;让原本由于接口…

YOLOv10改进,YOLOv10添加CA注意力机制,二次创新C2f结构,助力涨点

改进前训练结果: 二次创新C2f结构训练结果: 摘要 在本文中,提出了一种新的移动网络注意力机制,将位置信息嵌入到信道注意力中称之为“协调注意力”。与渠道关注不同通过 2D 全局池将特征张量转换为单个特征向量,坐标注意力因子将通道注意力转化为两个 1D 特征编码过程…

如何在AI时代成为优秀的AI产品经理?全面解析与全套学习路径分享!!!

前言 在当前人工智能技术飞速发展的时代背景下&#xff0c;AI产品经理无疑成为了职场中的一片蓝海。随着AI技术在各行各业的广泛应用&#xff0c;AI产品经理的角色变得越来越重要&#xff0c;成为了众多求职者眼中的优质赛道。那么&#xff0c;如何在AI的大环境下成为一名优秀…

李宏毅深度学习-自注意力机制

输入是向量序列的情况 在图像识别的时候&#xff0c;假设输入的图像大小都是一样的。但如果问题变得复杂&#xff0c;如图6.2所示&#xff0c;输入是一组向量&#xff0c;并且输入的向量的数量是会改变的&#xff0c;即每次模型输入的序列长度都不一样&#xff0c;这个时候应该…

搬砖 网盘一键转存源码

网盘一键转存源码&#xff0c;免费资源没测试 网盘一键转存源码&#xff0c;可以将您的百度网盘资源一键转存到。并支持后台设置开屏广告 源码截图&#xff1a; 下载地址&#xff1a; https://yuncv.lanzouw.com/i8dZk2btyl4h

六自由度机械重力补偿控制

1.动力学方程 六自由度机械臂动力学方程形式如下&#xff1a; 进行重力补偿&#xff0c;就是在驱动力矩中对重力G进行补偿&#xff0c;从而消除重力的影响&#xff0c;这样就能够在进行闭环控制的时候避免重力影响带来的大超调问题&#xff0c;使得机器人更好的实现轨迹跟踪控…

如何使用BlinkShot.io生成照片

在当今的数字时代&#xff0c;AI生成照片已经成为一项令人惊叹的技术。而BlinkShot.io就是这样一个平台&#xff0c;它可以让你轻松生成各种类型的照片。以下是详细步骤&#xff0c;教你如何使用BlinkShot.io生成照片。 第一步&#xff1a;访问网站 首先&#xff0c;打开Blin…