C语言--五子棋项目【图文详解 经典】

news2024/11/24 14:08:03

 今天小编带领大家学一学C语言入门必写的五子棋项目,题目非常经典,值得一学。


目录

 

一.目标效果

二.五子棋的元素

1.棋子

 2.棋盘

 三,需要准备的工具

四.具体内容

1.加载背景图片

2.画横线与竖线

3. 画小黑点

4.获取鼠标消息

 5.画棋子

6.如何判断比赛的输赢(核心)

 9.显示游戏结束的提示

 五.完整代码

 六.运行结果

​编辑果

 七.学习时的代码


一.目标效果🍗

能做出一个图形界面,能先下黑棋,后下白棋,能判断输赢,判断输赢后不会再下棋,会显示棋子赢的信息提示。

  • 背景知识(先稍微看一下)
  • 5*15条边,(15条边中间14个格子+左留白0.5+右留白0.5),一共15个格子,一个格子像素40
  • 窗口的大小 15*40=600(x)  600(y)
  • 在第4,12线交界点需要画黑点
  • 在第8线交界点需要画黑点
  • loadimage:用于从文件中读取图像。加载图片
  • putimage:显示图片
  • setlinecolor();
  • line:画线,需要两点坐标
  • setfillcolor:设置填充颜色
  • fillcircle:画填充(实心)圆,参数为坐标,半径
  • getmessage:获取鼠标消息
  • outtextxy:在指定位置(坐标)输出字符串
  •  settextcolor:设置字体颜色
  • settextstyle:设置字体样式

二.五子棋的元素🍗

1.棋子

 棋子分为黑白棋,双方对弈时要将棋子下到交叉点处。

棋盘的每一个交叉点位置设置为三种状态:

0表示没有棋子,1表示黑子,2表示白子。

定义一个全局的二维数组,int pieceArr[NUM][NUM] = { 0 };用来记录棋子的情况

int pieceArr[NUM][NUM] = { 0 };//记录15*15个棋盘的棋子情况,0表示没有棋子,1表示黑子,2表示白子

 2.棋盘🍗

 定义棋盘的线的数量与棋子个数。

#define NUM 15  //15条线
#define WIN_NUM 5//五子棋

棋盘有竖线15条,横线15条。在第4,12线交界点需要画黑点,在第8线交界点需要画黑点。

 三,需要准备的工具🍗

vs编译器是无法直接加载出图形界面,需要两个软件:

一.EasyX,图形(加载图形界面)

二.EasyX_Help( 帮助手册)

如果还不知道的小伙伴可以去看一下前篇文章,这里就不多赘述。

http://t.csdnimg.cn/0mnzJ

四.具体内容🍗

1.加载背景图片

首先要自己打开画图板,选择一种自己喜欢的颜色,涂好后保存。

保存图片。

点击五子棋项目,打开所在文件夹。并复制粘贴过去。

 然后点击查看EasyX的帮助手册

 

 

 

 最后就加载好背景图片了。


2.画横线与竖线

Draw_line();//画线

void Draw_line()
{
	setlinecolor(BLACK);
	//画竖线
	for (int x = 20; x < 600; x += 40)
		line(x, 20, x, 580);
	//画横线
	for (int y = 20; y < 600; y += 40)
		line(20, y, 580, y);
}

 运行结果如下:


3. 画小黑点

Draw_point();//画点

找到函数fillcircle,有三个参数,圆心坐标(x,y),半径大小。

画点
void Draw_point()
{
	setfillcolor(BLACK); //把线的颜色弄成黑色
	fillcircle(140, 140, 5), //坐标+半径
	fillcircle(140, 460, 5);
	fillcircle(460, 140, 5);
	fillcircle(460, 460, 5);
	fillcircle(300, 300, 5);
}

 效果是这样的

 


4.获取鼠标消息

 找到ExMessage,这个东西是用来保存鼠标消息的,是一个结构体。

 

还有一个getmessage,用来获取鼠标消息。

 我们再写一个死循环,用来给鼠标一直下棋,直到有一方获胜。

while (1)
{
	m = getmessage(EX_MOUSE);
	if (m.message == WM_LBUTTONDOWN) //如果访问的是鼠标向下,那么就画一个棋子
	{
        //画一个棋子
        //未完...
	}
   
}

 那么,鼠标可以获取消息了,那么怎么画棋子呢?


 5.画棋子

要解决的问题:

  • 如何先下黑棋,然后再下白棋呢?
  • 如何把棋子下到线与线交叉的位置?而不下到其它的位置呢?
  • 如何才能再下另一颗棋子时不覆盖先前下好的棋子呢?

  • 第一个问题:

定义一个bool类型,如果是黑棋就为真

bool black = true;//黑子先下
  •  第二个问题:

一开始,我们是用像素定义图像的大小,现在我们要把他换算为下标,即一个二维数组的下标,

定义一个全局的二维数组,int pieceArr[NUM][NUM] = { 0 };用来记录棋子的情况,0表示没有棋

子,1表示黑子,2表示白子。

void Draw_piece(bool black, int x, int y)
{
	//计算棋盘中的坐标
	int i = x / 40;
	int j = y / 40;

	if (black)//黑子
	{
		setfillcolor(BLACK);
		pieceArr[i][j] = 1; //黑棋为1
	}
	else //白子
	{
		setfillcolor(WHITE);
		pieceArr[i][j] = 2; //白棋为2
	}
	fillcircle(20 + i * 40, 20 + j * 40, 15); //画一个圆(x,y,半径)
}
  • 第三个问题:

定义一个好位置函数bool NicePos(int x, int y),如果这个位置是0,那么代表没有下过棋子,可以继续下,否则就不可以。

//判断这个位置是否有其他棋子
bool NicePos(int x, int y)
{
	//计算棋盘中的坐标
	int i = x / 40;
	int j = y / 40;
	return pieceArr[i][j] == 0;
}

6.如何判断比赛的输赢(核心)⭐

定义一个Gameover函数,int GameOver(int x, int y)

如果为0,游戏继续;

如果为1,黑棋赢;

如果为2,白棋赢;

 我们可以暴力地遍历整个二维数组,看有没有5个棋子是连在一起的,如果有,那么游戏结束。这样也行,但是时间会慢一些。

我们最优的解法是

只需要判断刚才下的棋子是否为5个即可
判断行,列,45度和135度斜线

定义一个n,用来保存棋子的颜色,如果为0则游戏继续。

定义一个计数器count,如果碰到一个棋子与自己一样,那么就++。

 我们需要判断四个方向,每个方向都要用一个for循环,如果与自己一样,那么计数器++,如果计数器大于等于5,那么就返回n。

//判断游戏胜利
int GameOver(int x, int y)
{
	x = x / 40;//换算为下标
	y = y / 40;//换算为下标
	
    int n = pieceArr[x][y];//得到棋子颜色
	if (n == 0)
		return 0;
	
    int count = 0;//统计棋子的数量
	
    int i, j; //当前棋子的前面或后面,或上面或下面,或斜向上或斜向下
	
	
     //同行的棋子
	for (i = x; i >= 0 && pieceArr[i][y] == n; i--)//同行的前面有没有相同的
		count++;
	for (i = x + 1; i < NUM && pieceArr[i][y] == n; i++)//同行的后面有没有相同的
		count++;
	if (count >= WIN_NUM)
		return n;

	
    //同列的棋子
	count = 0;//重新计算
	for (j = y; j >= 0 && pieceArr[x][j] == n; j--)//同列的上面有没有相同的
		count++;
	for (j = y + 1; j < NUM && pieceArr[x][j] == n; j++)
		count++;
	if (count >= WIN_NUM)
		return n;

	    
    //45度的棋子
	count = 0;//重新计算
	for (i = x, j = y; i < NUM && j >= 0 && pieceArr[i][j] == n; i++, j--)//右上
		count++;
	for (i = x - 1, j = y + 1; i >= 0 && j < NUM && pieceArr[i][j] == n; i--, j++)//左下
		count++;
	if (count >= WIN_NUM)
		return n;

	
    //135度的棋子
	count = 0;
	for (i = x, j = y; i >= 0 && j >= 0 && pieceArr[i][j] == n; i--, j--)//左上
		count++;
	for (i = x + 1, j = y + 1; i < NUM && j < NUM && pieceArr[i][j] == n; i++, j++)//右下
		count++;
	if (count >= WIN_NUM)
		return n;

	return 0;
}

  • 换算下标:

对x,y分别对40做取模运算就可以转换为下标了。


 9.显示游戏结束的提示

 使用这些函数函数outtextxy,settextstyle,outtextxy,具体内容读者可以自己查看手册。

while (1)
{
	m = getmessage(EX_MOUSE);
	if (m.message == WM_LBUTTONDOWN)
	{
		if (NicePos(m.x, m.y))
		{
			Draw_piece(black, m.x, m.y);
			int n = GameOver(m.x, m.y);
			if (n == 1)//黑子赢
			{
				settextcolor(RED);//字体颜色
				settextstyle(36, 0, _T("Consolas"));//字体样式
				outtextxy(250, 0, _T("黑棋赢了")); //输出文字内容
				break;
			}
			else if (n == 2)//白子赢
			{
				settextcolor(RED);
				settextstyle(36, 0, _T("Consolas"));
				outtextxy(250, 0, _T("白棋赢了"));
				break;
			}
			black = !black;
		}
	}
}

 五.完整代码🍗

#define _CRT_SECURE_NO_WARNINGS//这一句必须放在第一行
#include <graphics.h>		// 引用图形库头文件
#include <conio.h>
///*
//* * 15*15条边,(15条边中间14个格子+左留白0.5+右留白0.5),一共15个格子,一个格子像素为40
//* 则窗口的大小 15*40=600(x)  600(y)
//* //在第4,12线交界点需要画黑点
在第8线交界点需要画黑点
//* loadimage:用于从文件中读取图像。加载图片
//* putimage:显示图片
//* setlinecolor();
//* line:画线,需要两点坐标
//* setfillcolor:设置填充颜色
//* fillcircle:画填充(实心)圆,参数为坐标,半径
//* getmessage:获取鼠标消息
//* outtextxy:在指定位置(坐标)输出字符串
//* settextcolor:设置字体颜色
//* settextstyle:设置字体样式
//*
//*/
#define NUM 15
#define WIN_NUM 5//五子棋

int pieceArr[NUM][NUM] = { 0 };//记录15*15个棋盘的棋子情况,0表示没有棋子,1表示黑子,2表示白子

void Draw_line()
{
	setlinecolor(BLACK);
	//画竖线
	for (int x = 20; x < 600; x += 40)
		line(x, 20, x, 580);
	//画横线
	for (int y = 20; y < 600; y += 40)
		line(20, y, 580, y);
}

//画点
void Draw_point()
{
	setfillcolor(BLACK);
	fillcircle(140, 140, 5),
	fillcircle(140, 460, 5);
	fillcircle(460, 140, 5);
	fillcircle(460, 460, 5);
	fillcircle(300, 300, 5);
}

void Draw_piece(bool black, int x, int y)
{
	//计算棋盘中的坐标
	int i = x / 40;
	int j = y / 40;

	if (black)//黑子
	{
		setfillcolor(BLACK);
		pieceArr[i][j] = 1; //黑棋为1
	}
	else //白子
	{
		setfillcolor(WHITE);
		pieceArr[i][j] = 2; //白棋为2
	}
	fillcircle(20 + i * 40, 20 + j * 40, 15); //画一个圆(x,y,半径)
}

//判断这个位置是否有其他棋子
bool NicePos(int x, int y)
{
	//计算棋盘中的坐标
	int i = x / 40;
	int j = y / 40;
	return pieceArr[i][j] == 0;
}

//0:游戏继续,1:黑子赢; 2:白子赢
//只需要判断刚才下的棋子是否为5个即可
//判断行,列,45度和135度斜线


//判断游戏胜利
int GameOver(int x, int y)
{
	x = x / 40;//换算为下标
	y = y / 40;//换算为下标
	int n = pieceArr[x][y];//得到棋子颜色
	if (n == 0)
		return 0;
	int count = 0;//统计棋子的数量
	int i, j; //当前棋子的前面或后面,或上面或下面,或斜向上或斜向下
	
	//同行的棋子
	for (i = x; i >= 0 && pieceArr[i][y] == n; i--)//同行的前面有没有相同的
		count++;
	for (i = x + 1; i < NUM && pieceArr[i][y] == n; i++)//同行的后面有没有相同的
		count++;
	if (count >= WIN_NUM)
		return n;

	//同列的棋子
	count = 0;//重新计算
	for (j = y; j >= 0 && pieceArr[x][j] == n; j--)//同列的上面有没有相同的
		count++;
	for (j = y + 1; j < NUM && pieceArr[x][j] == n; j++)
		count++;
	if (count >= WIN_NUM)
		return n;

	//45度的棋子
	count = 0;//重新计算
	for (i = x, j = y; i < NUM && j >= 0 && pieceArr[i][j] == n; i++, j--)//右上
		count++;
	for (i = x - 1, j = y + 1; i >= 0 && j < NUM && pieceArr[i][j] == n; i--, j++)//左下
		count++;
	if (count >= WIN_NUM)
		return n;

	//135度的棋子
	count = 0;
	for (i = x, j = y; i >= 0 && j >= 0 && pieceArr[i][j] == n; i--, j--)//左上
		count++;
	for (i = x + 1, j = y + 1; i < NUM && j < NUM && pieceArr[i][j] == n; i++, j++)//右下
		count++;
	if (count >= WIN_NUM)
		return n;

	return 0;
}

int main()
{
	initgraph(600, 600);	// 创建绘图窗口,大小为 600x600 像素

	loadimage(NULL, _T("2.png")); //加载图片

	Draw_line();//画线
	
	Draw_point();//画点

	ExMessage m;//鼠标消息 ExMessage是一个结构体
	
	bool black = true;//黑子先下

	while (1)
	{
		m = getmessage(EX_MOUSE);
		if (m.message == WM_LBUTTONDOWN)
		{
			if (NicePos(m.x, m.y))
			{
				Draw_piece(black, m.x, m.y);
				int n = GameOver(m.x, m.y);
				if (n == 1)//黑子赢
				{
					settextcolor(RED);//字体颜色
					settextstyle(36, 0, _T("Consolas"));//字体样式
					outtextxy(250, 0, _T("黑棋赢了")); //输出文字内容
					break;
				}
				else if (n == 2)//白子赢
				{
					settextcolor(RED);
					settextstyle(36, 0, _T("Consolas"));
					outtextxy(250, 0, _T("白棋赢了"));
					break;
				}
				black = !black;
			}
		}
	}
	_getch();				// 按任意键继续
	closegraph();			// 关闭绘图窗口
	return 0;
}


 六.运行结果🍗


 七.学习时的代码🍗

观众老爷可以先把这一段代码拿去当模板,一点一点尝试去写,错了不要紧,代码就是一点一点改出来的,表示说一下子就可以写好。失败是成功之母,错误是正确他爹。慢慢来嘛。

#include <graphics.h>		// 引用图形库头文件
#include <conio.h>

#define NUM 15
#define WIN_NUM 5//五子棋

int pieceArr[NUM][NUM] = { 0 };//记录15*15个棋盘的棋子情况,0表示没有棋子,1表示黑子,2表示白子

//画线
void Draw_line()

//画点
void Draw_point()

//判断这个位置是否有其他棋子
bool NicePos(int x, int y)

//判断游戏胜利
int GameOver(int x, int y)

int main()
{
	initgraph(600,600);	// 创建绘图窗口,大小为 640x480 像素
	
	// 读取图片至绘图窗口
	loadimage(NULL, _T("2.png"));

	//画线
	Draw_line();
	
	//画点
	Draw_point();

	//鼠标消息
	ExMessage m; //结构体m ,用与存放鼠标消息

	//黑子先下
	bool black = true;//黑子先下

	_getch();				// 按任意键继续
	closegraph();			// 关闭绘图窗口
	return 0;
}

创作不易, 如果这份博客👍对你有帮助,可以给博主一个免费的点赞以示鼓励。
欢迎各位帅哥美女点赞👍评论⭐收藏,谢谢!!!
如果有什么疑问或不同的见解,欢迎在评论区留言哦👀。
祝各位生活愉快⭐

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

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

相关文章

【java学习—十五】创建多线程的两种方式(2)

文章目录 1. 多线程的创建和启动2. Thread 类3. 创建多线程的两种方式3.1.继承Thread类实现多线程3.1.1. 举例3.1.2. 举例2 3.2. 实现Runnable接口3.2.1. 举例13.2.2. 举例2 4. 继承方式和实现方式的联系与区别5. 使用多线程的优点 1. 多线程的创建和启动 Java 语言的 JVM 允许…

【差分演化算法相关文献总结】

差分演化算法相关文献总结 前言概述文献综述总结 前言 本人作为一名从事了三年演化算法研究的菜鸡研究生&#xff0c;其中大部分时间都在专注于差分演化算法&#xff08;Differential Evolution, DE&#xff09;的相关研究。现如今已经毕业&#xff0c;回顾往昔&#xff0c;经过…

跌破1940后金价直指1900 对黄金代理是好是坏?

受以鲍威尔为首的美联储官员近期讲话的影响&#xff0c;加上巴以冲突暂时出现降温&#xff0c;导致避险需求下降&#xff0c;在两大因素的影响之下&#xff0c;现货黄金行情在近期的大涨之后出现大跌。金价不光跌破1950关口&#xff0c;在跌穿1940后势头更是直指1900。金价在一…

虹科干货丨Lambda数据架构和Kappa数据架构——构建现代数据架构

文章来源&#xff1a;虹科云科技 虹科干货丨Lambda数据架构和Kappa数据架构——构建现代数据架构 如何更好地构建我们的数据处理架构&#xff0c;如何对IT系统中的遗留问题进行现代化改造并将其转变为现代数据架构&#xff1f;该怎么为你的需求匹配最适合的架构设计呢&#xf…

异常--Java

cry…catch使用 /*需求&#xff1a;测试除法器&#xff08;try...catch&#xff09;* 测试人&#xff1a;小王* 测试日期&#xff1a;2023/11/15* */ package yichang_test1;import java.util.InputMismatchException; import java.util.Scanner;public class TestException2 …

cadence virtuoso layout 无法跑DRC

问题&#xff1a;无法跑DRC could not establish connection with Calibre Interactiveon socket localhost 7000. 尝试&#xff1a; 点击一下红框右边的connect。 &#xff08;此法不一定有用&#xff0c;死马当活马医&#xff09;

Page分页records有数据,但是total=0,解决办法

Page分页records有数据&#xff0c;但是total0&#xff0c;解决办法 问题&#xff1a;程序运行起来后&#xff0c;后端接收前端传来的搜索请求信息正常&#xff0c;但无法在前端正确反馈信息&#xff0c;通过在后端排查发现total一直等于零&#xff0c;但数据库中有数据&#x…

大数据-之LibrA数据库系统告警处理(ALM-12046 网络写包丢包率超过阈值)

告警解释 系统每30秒周期性检测网络写包丢包率&#xff0c;并把实际丢包率和阈值&#xff08;系统默认阈值0.5%&#xff09;进行比较&#xff0c;当检测到网络写包丢包率连续多次&#xff08;默认值为5&#xff09;超过阈值时产生该告警。 用户可通过“系统设置 > 阈值配置…

OpenCV的应用——道路边缘检测

OpenCV&#xff08;Open Source Computer Vision Library&#xff09;是一个开源的计算机视觉库&#xff0c;它提供了丰富的图像处理和计算机视觉算法&#xff0c;使得开发者可以便捷地进行图像处理、对象识别、图像分割等任务。道路边缘检测是计算机视觉中的重要应用之一&…

帝国CMS仿核弹头H5小游戏模板/帝国CMS内核仿游戏网整站源码

帝国CMS仿核弹头H5小游戏模板&#xff0c;帝国CMS内核仿游戏网整站源码。比较适合小游戏发布、APP应用资讯类网站使用&#xff0c;有兴趣的可以二次开发试试。 下载地址&#xff1a;https://bbs.csdn.net/topics/617579435

uniapp基础学习笔记01

文章目录 本博客根据黑马教程学习uniapp一、技术架构二、创建项目2.1 Hbuilder创建2.2 插件安装2.3 微信开发者工具配置与运行2.3.1 简单修改基础页面 2.4 pages.json和tabBar2.4.1 pages.json与tabBar配置2.4.2 案例 三、uniapp与原生开发的区别 本博客根据黑马教程学习uniapp…

C# Socket通信从入门到精通(10)——如何检测两台电脑之间的网络是否通畅

前言: 我们在完成了socket通信程序开发以后,并且IP地址也设置好以后,可以先通过一些手段来测试两台电脑之间的网络是否通畅,如果确认了网络通畅以后,我们再测试我们编写的Socket程序。 1、同时按下键盘的windows键+"R"键,如下图: 下面两张图是两种键盘的情…

参考意义大。4+巨噬细胞相关生信思路,简单易复现。

今天给同学们分享一篇生信文章“Angiogenesis regulators S100A4, SPARC and SPP1 correlate with macrophage infiltration and are prognostic biomarkers in colon and rectal cancers”&#xff0c;这篇文章发表在Front Oncol期刊上&#xff0c;影响因子为4.7。 结果解读&a…

【探索Linux】—— 强大的命令行工具 P.15(进程间通信 —— system V共享内存)

阅读导航 引言一、system V的概念二、共享内存(1) 概念(2) 共享内存示意图(3) 共享内存数据结构 三、共享内存的使用1. 共享内存的使用步骤&#xff08;1&#xff09;包含头文件&#xff08;2&#xff09;获取键值&#xff08;ftok函数&#xff09;&#xff08;3&#xff09;创…

LeetCode - 142. 环形链表 II (C语言,快慢指针,配图)

如果你对快慢指针&#xff0c;环形链表有疑问&#xff0c;可以参考下面这篇文章&#xff0c;了解什么是环形链表后&#xff0c;再做这道题会非常简单&#xff0c;也更容易理解下面的图片公式等。 LeetCode - 141. 环形链表 &#xff08;C语言&#xff0c;快慢指针&#xff0c;…

写作脑科学——屠龙的高效写作指南

ISBN: 978-7-115-59231-6 作者&#xff1a;杨滢&#xff08;屠龙的胭脂井&#xff09; 页数&#xff1a;201页 阅读时间&#xff1a;2023-09-09 推荐指数&#xff1a;★★★★★ 十分推荐这本书&#xff0c;写的非常简单易懂&#xff0c;里面有很多方法论和实用技巧&#xff0c…

使用Maxent模型预测适生区

Maxent模型因其在潜在适生区预测中稳健的表现&#xff0c;时下已经成为使用最广泛的物种分布模型。biomod虽然可以通过集成模型的优势来弥补数据量较小的劣势&#xff0c;但是其在使用和运算时间上的优势远不如Maxent&#xff0c;虽然最新的biomod2已经修复了一些bug&#xff0…

Power Apps-使用power Automate流

创建&#xff1a;Power Automate-创建power Apps使用的流-CSDN博客 打开Power Apps&#xff0c;创建页面&#xff0c;添加三个输入框&#xff08;分别是换算前单位、换算后单位、货币数&#xff09;&#xff0c;和一个文本框&#xff08;输出结果&#xff09;以及一个按钮 在…

微信聚合聊天,自动回复

微信&#xff0c;这款融合通讯、社交、娱乐、小程序于一体的平台&#xff0c;已经深深融入我们的日常生活。作为我们日常生活中不可或缺的社交工具&#xff0c;尤其在工作中&#xff0c;我们需要通过微信来沟通客户&#xff0c;这个时候我们就会希望有快速回复客户的方式秒回客…

自动备份pgsql数据库

bat文件中的内容&#xff1a; PATH D:\Program Files\PostgreSQL\13\bin;D:\Program Files\7-Zip set PGPASSWORD**** pg_dump -h 8.134.151.187 -p 5466 -U sky -d mip_db --schema-only -f D:\DB\backup\%TODAY%-schema-mip_db_ali.sql pg_dump -h 8.134.151.187 -p 5466…