带你玩转三子棋—【C语言】

news2024/11/28 15:40:57

目录

前言:

1. 菜单的打印

2. game函数的实现

2.1 初始化棋盘

2.2 显示棋盘

2.3 玩家下棋

2.4 电脑下棋

2.5 判断输赢

2.6 判断棋盘是否满了

3. 全部代码

3.1 game.h

3.2  game.c

3.3 test.c


前言:

为了实现三子棋,首先我们应该将代码分模块编写,我们分为3个部分

1. test.c —测试游戏(主函数)2. game.c 实现游戏部分代码 3. game.h 函数的声明、宏定义等


1. 菜单的打印

菜单打印:

为了玩游戏方便,我们可以设置一个菜单,在每次玩游戏的时候都打印一下,方便玩家玩游戏体验,实现如下:

玩家选择 “1” -玩游戏       

玩家选择 “0”-退出游戏       

玩家选择其他数字—提示选择错误,重新选择



实现选择数字玩游戏:

1.如何做到玩完一把再玩一把,不想玩了还能退出游戏呢?

我们可以使用do-while循环,设置循环判断为输入的值,这样就实现了只有在输入为0时退出游戏


2. 如何做到选择1玩游戏、选择0退出游戏、选择其他数提示选择错误呢

我们可以在do-while循环的里面使用switch-case语句


代码如下:

 


2. game函数的实现

2.1 初始化棋盘

我们这里写的是三子棋的代码,三子棋有三行三列,我们可以使用二维数组来打印

我们可以遍历这个二维数组,把里面的9个元素都初始化为空格

代码:

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] = ' ';
		}
	}
}

2.2 显示棋盘

由于我们将上面二维数组的每个元素都初始化为空格,如果我们就这样打印出来给玩家玩游戏,那体验感是非常差的,所以,我们将用以下方法优化我们的棋盘显示,方便玩家玩

上面蓝色长方形框中: 

我们可以将三个空格(中间是数据,两边是空格)和后面的竖线归为1组,这一行有三组

注意:我们最后1组没有竖线(|),所以要另外控制下

上面黄色长方形框中:

我们将三个横线和一条竖线归为1组,一行也是三组,最后一行同理 

 代码实现:

总结:通过上面的分析,我们可以看出,每打印一行棋盘,我们其实是有两行的棋盘格式控制,所以,我们可以将蓝色和黄色框看成一组,一共有三组,最后一组不打印横线

void DisplayBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		//1. 打印数据
		int j = 0;
		for (j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			if(j<col-1)
				printf("|");
		}
		printf("\n");
		//2. 打印分割线
		if (i < row - 1)
		{
			//printf("---|---|---\n");
			int j = 0;
			for (j = 0; j < col; j++)
			{
				printf("---");
				if(j<col-1)
					printf("|");
			}
			printf("\n");
		}
	}
}

2.3 玩家下棋

三行三列的二维数组,行和列的下标范围都是0~2,玩家有可能不知道数组的性质,会出现数组下标越界的情况,这时我们需要将玩家输入的横竖坐标都进行减1处理

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] = '*';
				break;
			}
			else//不能落子
			{
				printf("坐标被占有,不能落子,重新输入坐标\n");
			}
		}
		else//非法
		{
			printf("坐标非法,重新输入\n");
		}
	}
}

2.4 电脑下棋

电脑下棋,我们可以使用生成随机数的函数,让生成的随机数%行、%列,就是一个随机的二维数组下标

生成随机数的函数用法,在我之前的博客有介绍,有兴趣可以去学习下哦!

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

	while (1)
	{
		x = rand() % row;
		y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

2.5 判断输赢

该函数要实现以下4种情况

1. 玩家赢

2. 电脑赢

3. 平局

4. 上面情况都不符合,那就继续下

赢的三种情况

1. 每一行出现三个相同的符号 2. 每一列出现三个相同的符号 3. 对角线出现三个相同的


平局:

实现一个判断这把游戏是不是平局的函数,在判断输赢函数中来调用

char IsWin(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 (IsFull(board, row, col) == 1)
	{
		return 'Q';
	}
	//继续
	return 'C';
}

2.6 判断棋盘是否满了

遍历数组,看是否有元素还是空格,如果有返回0,如果没有,那就表示棋盘满了,返回1

注意:这里返回的数字是返回到判断输赢函数的条件判断语句中

int IsFull(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;
}


3. 全部代码

3.1 game.h

#pragma once

#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 PlayerMove(char board[ROW][COL], int row, int col);
//电脑下棋
void ComputerMove(char board[ROW][COL], int row, int col);


//判断输赢
//玩家赢 - '*'
//电脑赢 - '#'
//平局  - 'Q'
//继续  - 'C'

char IsWin(char board[ROW][COL], int row, int col);

3.2  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] = ' ';
		}
	}
}

//打印棋盘
void DisplayBoard(char board[ROW][COL], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		//1. 打印数据
		int j = 0;
		for (j = 0; j < col; j++)
		{
			printf(" %c ", board[i][j]);
			if(j<col-1)
				printf("|");
		}
		printf("\n");
		//2. 打印分割线
		if (i < row - 1)
		{
			//printf("---|---|---\n");
			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] = '*';
				break;
			}
			else//不能落子
			{
				printf("坐标被占有,不能落子,重新输入坐标\n");
			}
		}
		else//非法
		{
			printf("坐标非法,重新输入\n");
		}
	}
}

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

	while (1)
	{
		x = rand() % row;
		y = rand() % col;
		if (board[x][y] == ' ')
		{
			board[x][y] = '#';
			break;
		}
	}
}

//判断棋盘是否已满
int IsFull(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 IsWin(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 (IsFull(board, row, col) == 1)
	{
		return 'Q';
	}
	//继续
	return 'C';
}

3.3 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};
	InitBoard(board, ROW, COL);
	//打印棋盘
	DisplayBoard(board, ROW, COL);
	//下棋
	char ret = 0;
	while (1)
	{
		//玩家下棋
		PlayerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);
		//判断输赢
		ret = IsWin(board, ROW, COL);
		if (ret != 'C')
			break;
		//电脑下棋
		ComputerMove(board, ROW, COL);
		DisplayBoard(board, ROW, COL);
		//判断输赢
		ret = IsWin(board, ROW, COL);
		if (ret != 'C')
			break;
	}
	if (ret == '*')
		printf("玩家赢\n");
	else if (ret == '#')
		printf("电脑赢\n");
	else
		printf("平局\n");
}

int main()
{
	int input = 0;
	srand((unsigned int)time(NULL));
	do
	{
		menu();
		printf("请选择:>");
		scanf("%d", &input);//1 0 4
		switch (input)
		{
		case 1:
			game();
			break;
		case 0:
			printf("退出游戏\n");
			break;
		default:
			printf("选择错误,重新选择!\n");
			break;
		}
	} while (input);

	return 0;
}


如果觉得文章不错,期待你的一键三连哦,你个鼓励是我创作的动力之源,让我们一起加油,顶峰相见!!! 

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

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

相关文章

frp将配置写在代码中重新打包

frp 是一个专注于内网穿透的高性能的反向代理应用&#xff0c;支持 TCP、UDP、HTTP、HTTPS 等多种协议。可以将内网服务以安全、便捷的方式通过具有公网 IP 节点的中转暴露到公网。在有些情况下我们需要隐藏配置信息&#xff0c;尤其是客户端&#xff08;比如我们要在第三方电脑…

第五章 总结及作业(123)【编译原理】

第五章 作业【编译原理】 前言推荐第五章 总结5.1自下而上分析基本问题 5.1.1归约5.1.2规范归约简述5.1.3 符号栈的使用与语法树的表示 5.2 算符优先分析5.2.1算符优先文法及优先表构造算法&#xff1a;构造FIRSTVT集算法&#xff1a;构造LASTVT集算法&#xff1a;构造优先表5.…

Google I/O 2023 - 一文快速总结 Flutter Dart 的现状和未来

随着 Google I/O 2023 的发布&#xff0c; Flutter 3.10 和 Dart 3.0 也都正式发&#xff0c;不得不说如今 Dart 的版本号终于快追上 Flutter 得版本号了&#xff0c;特别随着 Dart 3 的发布&#xff0c; Flutter 在 records 和 patterns 的加持下&#xff0c;开发体验终于开始…

LlamaIndex :面向QA 系统的全新文档摘要索引

在这篇博文中&#xff0c;我们介绍了一种全新的 LlamaIndex 数据结构&#xff1a;文档摘要索引。我们描述了与传统语义搜索相比&#xff0c;它如何帮助提供更好的检索性能&#xff0c;并通过一个示例进行了介绍。 背景 大型语言模型 (LLM) 的核心场景之一是对用户自己的数据进…

MapReduce框架

TextInputFormat 1&#xff09;FileInputFormat实现类 思考&#xff1a;在运行MapReduce程序时&#xff0c;输入的文件格式包括&#xff1a;基于行的日志文件、二进制格式文件、数据库表等。那么&#xff0c;针对不同的数据类型&#xff0c;MapReduce是如何读取这些数据的呢&…

Postman 如何关联接口测试并设置全局变量(带有token鉴权)

一、登陆接口 创建一个request请求 在Tests中添加JavaScript代码&#xff0c;用来获取鉴权&#xff1a; var jsonData JSON.parse(responseBody); var Authorization jsonData.data.access_token; console.log(Authorization) pm.globals.set(‘Authorization’,Authorizatio…

solr快速上手:solr简介及安装(一)

0. 引言 虽然现在主流的搜索引擎组件已经es主导&#xff0c;但不乏有部分“老”项目依旧在采用solr&#xff0c;当遇到这类项目时&#xff0c;如何快速上手solr组件&#xff0c;以及后续如何拓展深入研究solr的途径成为问题&#xff0c;本期我们的目的就是带大家来快速上手sol…

2023 年第三届长三角高校数学建模竞赛赛题浅析

为了更好地让大家本次长三角比赛选题&#xff0c;我将对本次比赛的题目进行简要浅析。数模模型通常分为优化、预测、评价三类&#xff0c;而本次数学题目就正好对应着A、B、C分别为优化、预测、评价。整体难度不大&#xff0c;主要难点在于A题的优化以及B、C的数据收集。稍后&a…

QT5.12.6 + mysql5.5.9 出现 Driver not loaded Driver not loaded

由于我重装了电脑系统&#xff0c;qt 和mysql均进行了软件版本的升级&#xff0c; 在使用数据库模块时&#xff0c;出现了如下问题&#xff1a; Driver not loaded Driver not loaded 排除问题一&#xff1a; pro文件中是否加载了sql模块 查看pro文件&#xff0c;发现 有此模…

React的两种组件创建方式(二)

react是面向组件编程的一种模式&#xff0c;它包含两种组件类型&#xff1a;函数式组件及类式组件 函数式组件 一个基本的函数组件长这个样子 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8" /><title>hell…

java报错-->java.lang.IllegalAccessError

1、前言 在gradle中运行main方法突然出现如下错误 Exception in thread "main" java.lang.IllegalAccessError: class XXX.util.ImageBorderUtils (in unnamed module 0x4bd4bcd4) cannot access class sun.font.FontDesignMetrics (in module java.desktop) becaus…

使用宝塔在Linux面板搭建网站,并实现公网远程访问「内网穿透」

文章目录 前言1. 环境安装2. 安装cpolar内网穿透3. 内网穿透4. 固定http地址5. 配置二级子域名6. 创建一个测试页面 转载自远程内网穿透的文章&#xff1a;Linux使用宝塔面板搭建网站&#xff0c;并内网穿透实现公网访问 前言 宝塔面板作为简单好用的服务器运维管理面板&#…

数据结构与算法(Java版) | 数组模拟队列的思路分析与代码实现

思路分析 上一讲我们讲过&#xff0c;队列既可以用数组来实现&#xff0c;也可以用链表来实现&#xff0c;但由于我们比较熟悉数组这种结构&#xff0c;所以这里我会先给大家讲一下数组这种实现方式&#xff0c;至于链表这种实现方式&#xff0c;那就以后有机会再说吧&#xf…

探索人工智能新纪元:Pre-Training 快速指南,轻松上手

theme: orange 预训练 Pre-Training 已被证明是当前人工智能范式中最重要的方面之一&#xff0c;大型语言模型&#xff0c;要转变为通用引擎&#xff0c;需要预训练。 什么是预训练模型 人工智能中的预训练至少部分受到人类学习方式的启发。我们不需要从零开始学习一个主题&…

肝一肝设计模式【七】-- 代理模式

系列文章目录 肝一肝设计模式【一】-- 单例模式 传送门 肝一肝设计模式【二】-- 工厂模式 传送门 肝一肝设计模式【三】-- 原型模式 传送门 肝一肝设计模式【四】-- 建造者模式 传送门 肝一肝设计模式【五】-- 适配器模式 传送门 肝一肝设计模式【六】-- 装饰器模式 传送门 文…

JavaWeb:Cookie、Session、JSP、JavaBean、MVC 三层架构

文章目录 JavaWeb - 03一、Cookie1. Cookie 应用2. 注意点 二、Session三、JSP1. 概述2. JSP 基础语法和指令&#xff08;了解&#xff09;3. 内置对象及作用域4. JSP 标签、JSTL 标签 四、JavaBean五、MVC 三层架构1. 之前的架构2. 现在的 MVC 三层架构 注意&#xff1a; Java…

Feign和OpenFeign

1.Feign和OpenFeign的关系 Feign Feign是一个声明式的Web服务客户端&#xff08;Web服务客户端就是Http客户端&#xff09;&#xff0c;让编写Web服务客户端变得非常容易&#xff0c;只需创建一个接口并在接口上添加注解即可。 Feign是Spring Cloud组件中一个轻量级RESTful的H…

MySQL基础学习---7、子查询

子查询 子查询指的是一个查询语句嵌套在另一个查询语句内部的查询&#xff08;从MySQL4.1开始引入&#xff09;。1、子查询的基本使用 语法格式&#xff1a;select select_listfrom tablewhere expr operator(select select_listfrom table); 说明&#xff1a;1、子查询&…

【终极解决方案】IDEA maven 项目修改代码不生效。

【终极解决方案】IDEA maven 项目修改代码不生效。 文章目录 【终极解决方案】IDEA maven 项目修改代码不生效。1、项目问题描述2、可能的解决方案3、分析原因4、解决方案5、参考文献 1、项目问题描述 遇到一个非常奇怪的问题&#xff0c;修改了一个基于maven搭建的SSM项目&am…

learn_C_deep_9 (汇编角度理解return的含义、const 的各种应用场景、volatile 的基本理解与实验证明)

目录 return 关键字 const 关键字 const 修饰的只读变量 - - - 不可直接被修改&#xff01; const修饰的变量&#xff0c;可以作为数组定义的一部分吗&#xff1f; const只能在定义的时候直接初始化&#xff0c;不能二次赋值。为什么&#xff1f; const修饰指针 volatil…