三子棋小游戏【C语言】

news2024/11/23 4:17:49

目录

前言

一、基本实现逻辑

二、实现步骤

1.实现进入游戏后的选择菜单——》这个实现起来较为容易,我们约定为:1是玩游戏, 0 是退出游戏!

2.要玩完了一局后接着玩,很容易想到要用循环。

3.采用多文件编程---》我们把实现游戏的相关代码放到game.c的文件里面,把所有头文件和函数的声明都放到game.h里面,谁用谁调用即可,把测试相关的代码都放到test.c里面。

4.创建棋盘

5.打印棋盘

6.玩家落子

7.电脑落子

8.胜负判断

三、全部游戏源码


前言

大家小时候,一定玩过三子棋也就是井字棋!它的玩法也很简单,让三个棋相连的时候就赢了!今天小编就用C语言来实现一下!如有不对的地方还请大正  小编先磕一个 Orz

一、基本实现逻辑

在实现前理清楚实现的顺序和逻辑,在实现中就能更好地实现,不至于一开始就一顿狂敲,到后面发现一堆bug这就得不偿失了!下面我们就来梳理一下实现该小游戏的逻辑:

1.我们希望进入游戏后可以有个菜单让我们选择

2.我们希望可以重复玩(一把玩完了还可以接着玩)

3.为了使每一个模块简洁,且调试起来方便我们采用多文件的形式

4.既然是下棋就得有个棋盘---》创建一个3*3的棋盘

5.打印棋盘

6.玩家落子(坐标形式)

7.电脑落子(随机)

8.判断胜负(玩家赢/电脑赢/平局)

二、实现步骤

1.实现进入游戏后的选择菜单——》这个实现起来较为容易,我们约定为:1是玩游戏, 0 是退出游戏!

代码演示:

#include<stdio.h>

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

int main()
{
	int input = 0;
	menu();
	printf("请输入操作数:> ");
	scanf("%d", &input);
	return 0;
}

这里就用一个这样的简陋的菜单作为选择界面!(小编不会用Easyx等的图形库)菜单弹出来之后,你得选择玩还是退出!因此需要一个变量来存你要选择的值!

2.要玩完了一局后接着玩,很容易想到要用循环。

但三种循环选择哪一种呢?我们想一开始你也得给玩家弹出彩单让他选择,玩或者是退出!也就是说无论是否玩都得给玩家弹出彩单让他选择!到这里大家都能想到了。没错,用do...while循环!接上面说的选择1 玩游戏,0 退出。我们改怎么处理呢?答案是用选择结构中的switch case 语句较好(if else也可以)。下面小编就来实现一下:

代码演示:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>

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

int main()
{
	int input = 0;
	do
	{
		menu();
		printf("请输入操作数:> ");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			printf("玩游戏!\n");
			break;
		case 0:
			printf("退出游戏!\n");
			break;
		default:
			printf("操作数非法,请重新输入!\n");
			break;
		}
	} while (input);
	return 0;
}

看效果:

3.采用多文件编程---》我们把实现游戏的相关代码放到game.c的文件里面,把所有头文件和函数的声明都放到game.h里面,谁用谁调用即可,把测试相关的代码都放到test.c里面。

看效果:

 这里要注意的是:一般引库里面的头文件我们用<>(例如:#include<stdlib.h>),而引用自己的头文件时用""。

4.创建棋盘

我们要下棋首先得有一个棋盘,我们想三子棋的棋盘实际上就是一个3*3的字符数组(该数组应该里面放的都是空格),在里面放上(覆盖空格)不同的字符而进行下棋的;那我们先创建一个3*3的字符数组,为了让我们看到先赋值为0,达到效果后改为' ';

代码演示:

 看效果:

  

这时候效果已经出来了,但有一个缺陷就是,这里的行和列已经写死了!如果后期有变动就改的东西很多!有没有更好地办法呢?答案是有的,我们前面初识C语言的时候说过一个东西叫#define定义的自符常量!用这个就可以!

void game()
{
	//创建棋盘
	char board[R][C] = { 0 };
	//初始化棋盘
	InitBoard(board, R, C);
	//打印棋盘
	DisplayBoard(board, R, C);
}

5.打印棋盘

上面已经创建弄好了棋盘,但还没有实现真正棋盘的打印,先来看看我们期盼的棋盘,

下来我们就来打印一下和他差不多的一个吧!

void DisplayBoard(char board[R][C], int r, int c)
{
	for (int i = 0; i < r; i++)
	{
		for (int j = 0; j < c; j++)
		{
			printf(" %c ", board[i][j]);
			if(j < c - 1)
				printf("|");
		}
		printf("\n");

		if (i < r - 1)
		{
			for (int j = 0; j < c; j++)
			{
				printf("---");
				if (j < c - 1)
					printf("|");
			}
			printf("\n");
		}

	}
}

看效果:

这样就基本模拟出来了一个棋盘!

6.玩家落子

由于小编也还是刚刚还没入门的小菜鸡,不会高端的技术,所以我们就采用简单的,玩家下棋我们就采用坐标,玩家输入要落子的位置(行、列都是1---3),这里我猜您有疑问,数组不是下标从0开始的吗,这里怎么是1开始的,我们要理解玩家,不可能每个玩家都是程序员吧!实现思路:遍历数组,当玩家的落子位置没有被占用(是空格),用 * 号覆盖空格,如果落子位置已经被占用了,提醒玩家重新落子!

代码演示:

void PlayerMove(char board[R][C], int r, int c)
{
	int x = 0; 
	int y = 0;
	printf("玩家下棋: \n");
	while (1)
	{
		printf("请输入落子坐标:> ");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= r && y >= 1 && y <= c)
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("此处已经被占用,请重新落子!\n");
			}
		}
		else
		{
			printf("落子坐标非法,请重新落子!\n");
		}
	}
}

当落子完之后给玩家反馈一下棋盘即落子后打印一遍棋盘!看效果:

OK玩家落子已解决!下面让我们来看看电脑落子吧!

7.电脑落子

电脑落子我们采用的是随机落子(在x,y都属于0---2),这里就不用x,y1--3了,因为是电脑,你让他怎样执行他就怎样执行!如何随机落子呢?我们想起来我们前面的猜数字游戏产生随机数的方法,上次是1--100,这次我们只要0-- 2即可!同样和上面玩家落子一样,得是 '  ' 时,才让落子。

代码演示:

void ComputerMove(char board[R][C], int r, int c)
{
	int x = 0;
	int y = 0;
	printf("电脑下棋: \n");
	while (1)
	{
		x = rand() % r;
		y = rand() % c;
		if (x >= 0 && x <= R && y >= 0 && y <= C)
		{
			if (board[x][y] == ' ')
			{
				board[x][y] = '#';
				break;
			}
		}
	}
}

ok!看效果:

经测试无问题!但现在面临的一个问题是他会无限制的落子找到棋盘满了,即使三个相同的相连了还不会停止,要解决这个问题,就要判断输赢! 

8.胜负判断

我们知道,三子棋当有三个相同的连起来时就结束了(某一方赢了),当棋盘满了还没有三个相同的相连时,就说明既不是玩家赢也不是电脑赢即平局了!如果棋盘没满就代表着还没有结束,继续下棋!

代码实现:

int IsFull(char board[R][C], int r, int c)
{
	for (int i = 0; i < r; i++)
	{
		for (int j = 0; j < c; j++)
		{
			if (board[i][j] == ' ')
			{
				return 0;
			}
		}
	}

	return 1;
}

//玩家赢返回'*' 电脑赢返回'#' 继续返回 'C' 平局返回'Q'
char IsWin(char board[R][C], int r, int c)
{
	//行
	for (int i = 0; i < r; i++)
	{
		if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
			return board[i][0];
	}

	//列
	for (int i = 0; i < c; 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[0][0];
	if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[2][0] != ' ')
		return board[2][0];

	//判断是否继续
	if (IsFull(board, r, c) == 1)
	{
		return 'Q';
	}
	
	return 'C';
}

我们来玩几把试一试:

三、全部游戏源码

game.h

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>
#include<time.h>
#include<stdlib.h>
#define R 3
#define C 3

//初始化棋盘
void InitBoard(char board[R][C], int r, int c);
//打印棋盘
void DisplayBoard(char board[R][C], int r, int c);
//玩家下棋
void PlayerMove(char board[R][C], int r, int c);
//电脑下棋
void ComputerMove(char board[R][C], int r, int c);
//判断输赢
//玩家赢返回'*'  电脑赢返回'#'  继续返回 'C'  平局返回'Q'
char IsWin(char board[R][C], int r, int c);

game.c

#include"game.h"

void InitBoard(char board[R][C], int r, int c)
{
	for (int i = 0; i < r; i++)
	{
		for (int j = 0; j < c; j++)
		{
			board[i][j] = ' ';
		}
	}
}

void DisplayBoard(char board[R][C], int r, int c)
{
	for (int i = 0; i < r; i++)
	{
		for (int j = 0; j < c; j++)
		{
			printf(" %c ", board[i][j]);
			if(j < c - 1)
				printf("|");
		}
		printf("\n");

		if (i < r - 1)
		{
			for (int j = 0; j < c; j++)
			{
				printf("---");
				if (j < c - 1)
					printf("|");
			}
			printf("\n");
		}

	}
}


void PlayerMove(char board[R][C], int r, int c)
{
	int x = 0; 
	int y = 0;
	printf("玩家下棋: \n");
	while (1)
	{
		printf("请输入落子坐标:> ");
		scanf("%d %d", &x, &y);
		if (x >= 1 && x <= r && y >= 1 && y <= c)
		{
			if (board[x - 1][y - 1] == ' ')
			{
				board[x - 1][y - 1] = '*';
				break;
			}
			else
			{
				printf("此处已经被占用,请重新落子!\n");
			}
		}
		else
		{
			printf("落子坐标非法,请重新落子!\n");
		}
	}
}	


void ComputerMove(char board[R][C], int r, int c)
{
	int x = 0;
	int y = 0;
	printf("电脑下棋: \n");
	while (1)
	{
		x = rand() % r;
		y = rand() % c;
		if (x >= 0 && x <= R && y >= 0 && y <= C)
		{
			if (board[x][y] == ' ')
			{
				board[x][y] = '#';
				break;
			}
		}
	}
}


int IsFull(char board[R][C], int r, int c)
{
	for (int i = 0; i < r; i++)
	{
		for (int j = 0; j < c; j++)
		{
			if (board[i][j] == ' ')
			{
				return 0;
			}
		}
	}

	return 1;
}

//玩家赢返回'*' 电脑赢返回'#' 继续返回 'C' 平局返回'Q'
char IsWin(char board[R][C], int r, int c)
{
	//行
	for (int i = 0; i < r; i++)
	{
		if (board[i][0] == board[i][1] && board[i][1] == board[i][2] && board[i][0] != ' ')
			return board[i][0];
	}

	//列
	for (int i = 0; i < c; 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[0][0];
	if (board[0][2] == board[1][1] && board[1][1] == board[2][0] && board[2][0] != ' ')
		return board[2][0];

	//判断是否继续
	if (IsFull(board, r, c) == 1)
	{
		return 'Q';
	}
	
	return 'C';
}

test.c

#include"game.h"

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

void game()
{
	//创建棋盘
	char board[R][C] = { 0 };
	//初始化棋盘
	InitBoard(board, R, C);
	//打印棋盘
	DisplayBoard(board, R, C);
	char ret = 0;
	while (1)
	{
		//玩家下棋
		PlayerMove(board, R, C);
		DisplayBoard(board, R, C);
		//判断输赢
		ret = IsWin(board, R, C);
		if (ret != 'C')
			break;
		//电脑下棋
		ComputerMove(board, R, C);
		DisplayBoard(board, R, C);
		//判断输赢
		ret = IsWin(board, R, C);
		if (ret != 'C')
			break;
	}

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

}

int main()
{
	srand((unsigned int)time(NULL));
	int input = 0;
	do
	{
		menu();
		printf("请输入操作数:> ");
		scanf("%d", &input);
		switch (input)
		{
		case 1:
			game();
			//printf("玩游戏!\n");
			break;
		case 0:
			printf("退出游戏!\n");
			break;
		default:
			printf("操作数非法,请重新输入!\n");
			break;
		}
	} while (input);

	return 0;
}

OK!好兄弟我们下期再见!

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

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

相关文章

0011-TIPS-pawnyable : Heap-Overflow

原文 Linux Kernel PWN | 040202 Pawnyable之堆溢出 Holstein v2: Heap Overflowの悪用 题目下载 漏洞代码 #include <linux/module.h> #include <linux/kernel.h> #include <linux/cdev.h> #include <linux/fs.h> #include <linux/uaccess.h>…

【Nginx介绍和安装与升级】

文章目录 Nginx网站服务一、nginx服务基础1. Nginx简介2. Tengine3. Nginx 相对于 Apache 的优点4. 阻塞与非阻塞 同步与异步5. nginx 应用场景 二、编译安装nginx 服务1. 在线安装nginx2. nginx编译安装1. 关闭防火墙&#xff0c;将安装nginx所需软件包传到/opt目录下2. 安装依…

二维码标签制作教程

如今二维码已被广泛应用在了许多场景中&#xff0c;譬如设备巡检、固定资产盘点、隐患上报、人员管理等&#xff0c;在二维码投入使用前需要为设备、物品、人员等制作一物一码标签。为了让标签快速落地&#xff0c;可使用草料二维码的标签制作功能&#xff0c;只需选择心仪的标…

Cortext-M3系统:中断的具体行为(6)

1、中断/异常的响应序列 当CM3开始响应一个中断时&#xff0c;会在它小小的体内奔涌起三股暗流&#xff1a; ​ 入栈&#xff1a;把8个寄存器的值压入栈 ​ 取向量&#xff1a;从向量表中找出对应的服务程序入口地址 ​ 选择堆栈指针MSP/PSP&#xff0c;更新堆栈指针SP&#xf…

数据恢复技巧:如何恢复已删除的手机短信

短信包含大量重要信息&#xff0c;例如个人联系人、密码和重要业务数据。丢失有价值的数据可能会令人失望&#xff0c;但很高兴知道可以使用多种方法恢复短信。 在本文中&#xff0c;我们将探讨您可以采取哪些步骤来恢复 Android 或 iOS 设备上丢失或删除的短信。 第 1 部分…

chatgpt赋能python:Python指定区域截图:优化截图流程的最佳方式

Python指定区域截图&#xff1a;优化截图流程的最佳方式 在网络时代&#xff0c;图片作为一种重要的信息传播方式&#xff0c;扮演着举足轻重的角色。截图作为最常见的图片处理方式之一&#xff0c;也时常被我们用于记录屏幕内容、报告问题及展示操作流程。 若想提高截图的效…

操作系统-I/O管理-磁盘和固态硬盘

目录 一、磁盘结构 二、磁盘调度算法 ​编辑 2.1先来先服务(FCFS) 2.2最短寻找时间优先(SSTF) 2.3扫描算法(SCAN) 2.4LOOK调度算法 2.5循环扫描算法(C-SCAN) 2.6 C-LOOK调度算法 三、减少延迟时间方法 交替编号 ​磁盘地址结构的设计 错位命名 四、磁盘的管理 4.1磁盘…

JDK自带的构建线程池的方式之newFixedThreadPool

在Java中基于Executors提供了很多种线程池供开发者使用&#xff0c;在Java的并发包下&#xff0c;由并发编程大佬到隔离创建。 newFixedThreadPool 这个线程池的特点是线程数是固定的&#xff0c;下面这个是在Executors类中提供的一种静态方法。在使用的时候需要向方法提供一个…

Excel VBA 语法基础

VBA&#xff08;Visual Basic for Applications&#xff09;是一种用于宏编程和自动化任务的编程语言&#xff0c;广泛应用于 Microsoft Office 套件中的各种应用程序&#xff0c;如 Excel、Word 和 PowerPoint。掌握 VBA 基础语法可以帮助您通过编写自定义的宏来增强和自动化这…

python:使用Scikit-image对遥感影像进行角点检测特征提取(corner)

作者:CSDN @ _养乐多_ 本文将介绍使用Scikit-image对遥感影像进行角点检测特征(corner)提取的一些方法及其代码。方法包括 Harris角点检测(corner_harris),Shi-Tomasi角点检测(corner_shi_tomasi),Foerstner角点检测(corner_foerstner),FAST角点检测(corner_fast…

VUE 2X 计算监视属性 ⑥

目录 文章有误请指正&#xff0c;如果觉得对你有用&#xff0c;请点三连一波&#xff0c;蟹蟹支持✨ V u e j s Vuejs Vuejs计算属性 C o m p u t e d Computed Computed监视属性 W a t c h Watch Watch总结 文章有误请指正&#xff0c;如果觉得对你有用&#xff0c;…

Apache Superset产品调研

Apache Superset产品调研 调研报告&#xff1a;Apache Superset 一、概述 Apache Superset是一个开源的数据可视化和数据探索平台&#xff0c;它提供了一个用户友好的界面&#xff0c;可以轻松地创建和分享仪表板。它支持多种数据源&#xff0c;包括SQLAlchemy兼容的数据库、…

io.netty学习(九)Netty 如何实现零拷贝

目录 前言 Java 实现零拷贝 1、Java提供 mmap/write 方式 2、Java 提供 sendfile 方式 Netty 实现零拷贝 1、CompositeByteBuf 方式 2、wrap 方式 3、slice 方式 4、 FileRegion 方式 总结 前言 本篇文章我们就来讲讲 Netty 的零拷贝&#xff0c;在这之前&#xff0…

数据结构练习题1:基本概念

练习题1&#xff1a;基本概念 1 抽象数据类型概念分析2. 逻辑结构与存储结构概念分析3.综合选择题4.综合判断题5.时间复杂度相关习题 1 抽象数据类型概念分析 1.可以用&#xff08;抽象数据类型&#xff09;定义一个完整的数据结构。 分析&#xff1a; 1&#xff09;抽象数据…

【Leetcode -617.合并二叉树 -1022.从根到叶的二进制数之和】

Leetcode Leetcode -617.合并二叉树Leetcode -1022.从根到叶的二进制数之和 Leetcode -617.合并二叉树 题目&#xff1a;给你两棵二叉树&#xff1a; root1 和 root2 。 想象一下&#xff0c;当你将其中一棵覆盖到另一棵之上时&#xff0c;两棵树上的一些节点将会重叠&#x…

ESP32-IDF VS Code进行开发

ESP32-C3 入门篇&#xff08;二&#xff09;使用VS Code进行开发 文章目录 前言 总结STM32的成功&#xff0c;除了Cortex M3的性能强之外&#xff0c;也离不开ST推出的标准库及简单易用的MDK IDE。完善的开发配套&#xff0c;极大的降低了开发门槛&#xff0c;让开发者更专注…

python:使用Scikit-image对遥感影像做空间滤波(中值,高斯,Sobel,Laplace,Scharr等)

作者:CSDN @ _养乐多_ 本文将介绍使用Scikit-image中的滤波函数对遥感影像做空间滤波的代码。滤波方法包括:中值滤波器,高斯滤波器,Sobel滤波器,Laplace滤波器,Scharr滤波器等。并将原始影像和结果影像绘制成图。 结果如下图所示, 文章目录 一、空间滤波函数详解二、…

RocketMQ发送消息

一.消费模式 MQ的消费模式可以大致分为两种&#xff0c;一种是 推Push&#xff0c;一种是 拉Pull。 Push 是 服务端 (MQ) 主动推送消息给客户端&#xff0c;优点是及时性较好&#xff0c;但如果客户端没有做好流控&#xff0c;一旦服务端推送大量消息到客户端时&#xff0c;…

分布式光伏电站智能管理系统

随着能源需求的增加&#xff0c;各种各样的光伏电站工程建设出现不同形式的技术缺陷。设计了分布式光伏电站区域智能系统&#xff0c;实现 了各个园区用电数据的集中管理。对光伏电站分布式运营管理进行了研究&#xff0c;采用集中运营管理中心的方法&#xff0c;建立了区域分布…

蓝牙L2CAP协议简介及报文格式

概述 逻辑链路控制和适配协议&#xff08;Logical Link Control and Adaptation Protocol&#xff0c;L2CAP&#xff09;是蓝牙的核心协议&#xff0c;负责适配基带中的上层协议。它同链路管理器并行工作&#xff0c;向上层协议提供定向连接的和无连接的数据业务。L2CAP具有分…