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

🚩纸上得来终觉浅, 绝知此事要躬行。
🌟主页: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");
	}
}

❤️ 结语

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

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

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

相关文章

【SpringCloud】Gateway使用

文章目录 概述阻塞式处理模型和非阻塞处理模型概念阻塞式处理模型 三大核心概念 工作流程使用POMYML启动类配置路由通过编码进行配置动态路由常用的Route Predicate自定义全局过滤器自定义filter 官网 https://cloud.spring.io/spring-cloud-static/spring-cloud-gateway/2.2.1…

学习笔记230801--vue项目图片绝对路径和相对路径引入编译加载问题

问题描述&#xff1a; 在组件中引入图片出现了问题,<img>标签的src属性&#xff0c;动态绑定import引入的绝对路径图片或者直接在src静态引入图片绝对路径都可以在页面渲染出来&#xff0c;在浏览器可以看到路径都转成了dataUrl&#xff0c;但是动态绑定图片的绝对路径却…

Redis 缓存满了怎么办?

引言 Redis 缓存使用内存来保存数据&#xff0c;随着需要缓存的数据量越来越大&#xff0c;有限的缓存空间不可避免地会被写满。此时&#xff0c;应该怎么办&#xff1f;本篇文章接下来就来聊聊缓存满了之后的数据淘汰机制。 值得注意的是&#xff0c;在 Redis 中 过期策略 和…

MySQL高级篇——MySQL架构篇2(MySQL的数据目录)

目录 1 MySQL8的主要目录结构1.1 数据库文件的存放路径1.2 相关命令目录1.3 配置文件目录 2 数据库和文件系统的关系2.1 查看默认数据库2.2 数据库在文件系统中的表示2.3.1 InnoDB存储引擎模式2.3.2 MyISAM存储引擎模式 2.4 总结2.5 视图在文件系统中的表示2.6 其他的文件 1 My…

软件测试下的AI之路(1)

&#x1f60f;作者简介&#xff1a;博主是一位测试管理者&#xff0c;同时也是一名对外企业兼职讲师。 &#x1f4e1;主页地址&#xff1a;【Austin_zhai】 &#x1f646;目的与景愿&#xff1a;旨在于能帮助更多的测试行业人员提升软硬技能&#xff0c;分享行业相关最新信息。…

leetcode1094. 拼车(差分数组-java)

差分数组 leetcode 1094 拼车差分数组代码演示&#xff1a; 前缀和数组 leetcode 1094 拼车 难度 - 中等 原题链接 - 拼车 车上最初有 capacity 个空座位。车 只能 向一个方向行驶&#xff08;也就是说&#xff0c;不允许掉头或改变方向&#xff09; 给定整数 capacity 和一个数…

CSSCI、北核期刊投稿指南(2023年更新)

该数据为经管类的期刊投稿指南&#xff0c;包含发表难度&#xff0c;文章数量&#xff0c;影响因子&#xff0c;用户评价等指标。共5份文件&#xff0c;分别为国内所有期刊信息库、投稿指南&#xff08;CSSCI版本、CSSCI扩展版本、北大核刊版本、建议期刊版本&#xff09; 一、…

框架(Git基础详解及Git在idea中集成步骤)

目录 基础&#xff1a; idea集成Git并添加项目到git仓库 1.idea集成git&#xff0c;集成.git.exe文件 2.初始化本地Git仓库项目 3. 将工作区代码添加到暂存区 4.将暂存区代码添加到本地仓库 5.Git本地库操作 Idea集成Gitee并提交代码到第三方库 1.setting里搜索gitee 2.添…

【HCIP】04.VRRP与BFD

VRRP VRRP基本概念 VRRP路由器 运行VRRP协议的路由器&#xff0c;VRRP是配置在路由器的接口上的&#xff0c;而且也是基于接口来工作的。 VRID 一个VRRP组由多台协同工作的路由器&#xff08;的接口&#xff09;组成&#xff0c;使用相同的VRID&#xff08;Virtual Router…

JavaScript-console:JavaScript控制台(Console)常用方法

一、理解 console JavaScript 控制台&#xff08;console&#xff09;是一个开发人员在编写 JavaScript 代码时常用的工具。它是浏览器提供的一种界面&#xff0c;让开发人员能够追踪代码执行的状态和结果。JavaScript 控制台可以记录代码输出的信息、警告和错误&#xff0c;并…

2023-08-21 LeetCode每日一题(移动片段得到字符串)

2023-08-21每日一题 一、题目编号 2337. 移动片段得到字符串二、题目链接 点击跳转到题目位置 三、题目描述 给你两个字符串 start 和 target &#xff0c;长度均为 n 。每个字符串 仅 由字符 ‘L’、‘R’ 和 ‘_’ 组成&#xff0c;其中&#xff1a; 字符 ‘L’ 和 ‘R…

ISVE2023展商 | 皮克智能:零售及互联网领域数字化变革开拓者

ISVE2023展商 | 皮克智能&#xff1a;零售及互联网领域数字化变革开拓者 01 公司介绍 Exhibitor Profile 皮克智能是优质的智能硬件产品及系统解决方案提供商&#xff0c;具备自主研发创新、软硬件方案集成及全产业链资源整合的能力。 公司总部位于中国深圳&#xff0c;主要服…

爬虫逆向实战(十九)--某号站登录

一、数据接口分析 主页地址&#xff1a;某号站 1、抓包 通过抓包可以发现登录接口 2、判断是否有加密参数 请求参数是否加密&#xff1f; 通过查看“载荷”模块可以发现有一个jsondata_rsa的加密参数 请求头是否加密&#xff1f; 无响应是否加密&#xff1f; 无cookie是否…

华为OD机试之报文重排序【Java源码】

题目描述 对报文进行重传和重排序是常用的可靠性机制&#xff0c;重传缓中区内有一定数量的子报文&#xff0c;每个子报文在原始报文中的顺序已知&#xff0c;现在需要恢复出原始报文。 输入描述 输入第一行为N&#xff0c;表示子报文的个数&#xff0c;0 &#xff1c;N ≤ …

Vue3组合式API详解 - 大型应用的高端写法

目录 01-setup方法与script_setup及ref响应式02-事件方法_计算属性_reactive_toRefs03-生命周期_watch_watchEffect04-跨组件通信方案provide_inject05-复用组件功能之use函数06-利用defineProps与defineEmits进行组件通信 01-setup方法与script_setup及ref响应式 在Vue3.1版本…

python 基础篇 day 1 初识变量和数据类型

文章目录 变量变量作用——用于存储和表示数据。变量命名规则命名法大驼峰小驼峰下划体n j i a x 通常作为临时变量使用 建议 变量种类全局变量&#xff08;Global Variables&#xff09;局部变量&#xff08;Local Variables&#xff09;静态变量&#xff08;Static Variables…

CentOS下MySQL的彻底卸载的几种方法

这里我为大家详细讲解下“CentOS下MySQL的彻底卸载的几种方法”的完整攻略。 一、关闭MySQL服务 在开始操作之前&#xff0c;需要先关闭MySQL服务。可以使用以下命令来关闭MySQL服务&#xff1a; systemctl stop mysqld 或者 service mysqld stop 二、使用yum命令卸载MySQL…

【C++】STL——set的介绍和使用、set的构造函数、set的迭代器、set的容量和增删查改函数

文章目录 1.set的介绍2.set的使用2.1set的构造函数2.2set的迭代器2.3set的容量函数2.4set的增删查改函数 1.set的介绍 set的介绍 &#xff08;1&#xff09;set是按照一定次序存储元素的容器。 &#xff08;2&#xff09;在set中&#xff0c;元素的value也标识它(value就是key…

最新ChatGPT网站程序源码+AI系统+详细图文搭建教程/支持GPT4.0/AI绘画/H5端/Prompt知识库

一、前言 SparkAi系统是基于国外很火的ChatGPT进行开发的Ai智能问答系统。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。 那么如何搭建部署AI创作ChatGPT&#xff1f;小编这里写一个详细图文教程吧&#xff01…

PDF怎么转Word?8 个最佳 PDF 转 Word 转换器

PDF 转 Word 转换工具只是一个特殊程序&#xff0c;可以将 PDF&#xff08;本机和/或扫描&#xff09;转换为 Microsoft Office Word 格式。将 PDF 导出到 Word 的主要原因之一是满足可编辑文档的需求&#xff0c;尽管还有其他原因。 由于缺少 PDF 阅读器&#xff0c;您可以选…