C++项目

C++数独游戏

大作业,每一步都有解释,看不懂请评论区留言。
本人是个编程草包小白,这是我第一次发博客
这个是第一个版本,还有第二个。
准备工具VS2022,easyX库
vs2022通常有检测不到的情况。vs2019没有
VS2022的easyX检测不到的可以,先下载VS2019,再检测,可以利用VS2022和VS2019文件路径相同的便利,在VS2022中实现。

思路

分文件编写,多文件开发
利用Ctrl+.快捷键,在头文件中快速创建和重构
界面类widget.h,——>widget.cpp
按钮类button.h——>button.cpp
数独类suduku.h——>sudoku.cpp
main()测试案例

请添加图片描述

本人不知道如何将ppt讲评传入blog中。。。。。。。

文字描述

  • 1.首先创建commmon.h包含所有用到的库。比如说vector库,动态数组,sstream流文件

    2.流程为首先创建界面头文件,设置高度宽度,用宏定义在common中设置#define WIDTH 60 #define HEIGHT 60 为固定界面Widget w(9 * WIDTH, 9 * HEIGHT); initgraph:初始化图像系统
    closegraph : 关闭图形系统。(在析构函数中调用) 并且在main()中调用w.Show();显示界面

    3.其次,按钮类的设置 (1)坐标,大小,颜色,字体 (2)鼠标是否在上面MouseInButton 鼠标是否点击了按钮ClickButton (3)void SetText();测试

    4.数独类逻辑规则 (1)首先创建99动态数组,也就是数独题目 vector<pair<bool, Button>> rect; 并且把按钮类嵌套进去 数独库里面, //存放按钮,为每一个按钮配置一个可否修改的标记bool
    vector<pair<bool, Button*>> rect; (2)逻辑 横向,纵向,九宫格内部。

    5.时间表调时间#include<timer>

废话不多了,说直接上代码和教学

首先创建一个公共头文件,方便调用C++的库函数和easyX的库函数和各种各样的功能性库函数。
注意:下面每一个源文件的函数都只是在头文件中之间选取函数右键选择快速操作和重构
快捷键ctrl+.

1.common.h

#pragma once//防止头文件重复包含
#include <iostream>//源文件库,不多解释
#include <string>//字符串库,单个数字也是字符串的一部分
#include <graphics.h>//图像处理库,含有大量的工具函数
#include <array>//相比于数组,array可以使用成员函数
#include <vector>//动态数组,储存9*9的数字,还有把类嵌入进去的作用
#include <sstream>//流文件,字符串和整型互相转换
#include <ctime> using namespace std;
#define WIDTH 60//界面固定宽度和高度
#define HEIGHT 60

2.weight.h

#pragma once
#include "common.h"//导入公共头文件,可导可不导
//窗口类
class Widget 
{
public:
	Widget(int width,int height);//界面高度和宽度
	void Show();		//显示窗口
	~Widget();
protected://保护权限,外界不可以访问,因为这个东西在common已经把这两个东西写死了,界面高度和宽度无法改变,这里只是展示界面的作用,举个例子,就像坐标系意义,你固定了在封闭曲面单位圆里面,区域为D,-1<<x<<1,-1<<y<<1。但是归根结底x,y都为变量
	int width;
	int height;
};

3.weight.cpp

#include "widget.h"
#include "common.h"
//widget先定义后创建
Widget::Widget(int width, int height)
{
	this->width = width;//this防止变量重名
	this->height = height;
}
void Widget::Show()
{
	initgraph(width, height);//界面初始化
}
Widget::~Widget()//析构销毁,
{
	closegraph();//析构销毁,关闭界面,其实不写也可以,我调试
}

button.h

#pragma once
#include "common.h"
class Button
{
public://传入按钮参数,有多少传多少
	Button(int x = 0, int y = 0, int w = 60, int h = 60, COLORREF curColor = LIGHTGRAY, string test = "");
	//按钮坐标初始化x=0,y=0,按钮宽度高度=60,因为界面大小为9*60=540平方,把按钮设成60*60的规格大小刚刚好可以铺满整个界面,颜色深灰,字符串
	
	void Show();		//显示按钮

	bool MouseInButton(ExMessage& msg);  //鼠标是否在按钮,ExMessage& msg鼠标信息,

	bool ClickButton(ExMessage& msg);	//判断当前点击了鼠标

	void SetText();//测试案例

protected://先在保护权限里面写x,y,w,h,颜色参数
	//按钮画在窗口的按钮个位置
	int x;
	int y;
	//按钮宽度和高度
	int w;
	int h;

	COLORREF curColor;//鼠标放在里面的颜色,COLORREF =typedef unsigned long       DWORD;无符号的长整型
	COLORREF outColor;//暴露的颜色,可写可不写。
	string text;//按钮的文字,(这里不是int 的数字),
};

button.cpp

#include "button.h"
Button::Button(int x, int y, int w, int h, COLORREF curColor, string text)
{
	this->x = x;
	this->y = y;
	this->w = w;
	this->h = h;
	this->curColor = curColor;//当前颜色
	this->text = text;
	this->outColor = curColor;//当前颜色保存到outColor,(权)
}
void Button::Show()
{
	//画一个矩形出来
	setfillcolor(this->curColor);//矩形填充颜色,引入颜色
	setlinecolor(BLACK);//线的颜色为为黑色
	fillrectangle(x, y, x + w, y + h);//矩形坐标左上角为(x,y),右下角为(x+w,y+h)

	//把文字显示出来
	settextstyle(h * 2/ 3, 0, "黑体");//文字样式为高度为矩形框的2/3,宽度自适应,字体为黑体
	setbkmode(TRANSPARENT);//设置数字背景透明,文字才可以更明显
	settextcolor(BLACK);//文字黑色

	//文字坐标xx,yy计算
	int xx = x + (w - textwidth(text.c_str()))/2;//按钮x+((按钮宽度-文字宽度))/2
	int yy = y + (w - textheight(text.c_str()))/2;//textheight为int 整型的变量名,在图像库里面功能是高度,c_str是指针名(也是变量名)
	outtextxy(xx, yy, text.c_str());//文字显示函数,坐标和文字
}
bool Button::MouseInButton(ExMessage& msg)//鼠标放入函数
{
	if (msg.x > x && msg.x<x + w && msg.y>y && msg.y < y + h) //鼠标的x的坐标是否大于按钮的左上角坐标(x),并且小于右下角坐标(x+w)。 可以下面有图解
	{//如果是,则鼠标在里面,按钮变颜色为深蓝色
		this->curColor = LIGHTBLUE;				//鼠标在按钮中改变颜色
		return true;//结束函数,没有这句就会还原,鼠标点击直接卡死
	}
	this->curColor = this->outColor;			//鼠标偶不在按钮中还原颜色
	return false;
}

bool Button::ClickButton(ExMessage& msg)//鼠标点击函数
{
	if (MouseInButton(msg) && msg.message==WM_LBUTTONUP) //鼠标按下弹起的时候就return结束了
	{
		return true;
	}
	return false;
}
void Button::SetText()//点击文字更改,流构建成类型转换为整数
{
	stringstream stream(text);//把文字传进来
	int num = 0;
	stream >> num;		//把字符串转换为整数,num称为权,因为只有整型的int num;可以从0改到9,字符串做不到的。
	num++;
	if (num > 9) 
	{
		num = 0;//数字大于9,从0再来
	}
	this->text = to_string(num);	//还原成字符串,再把整数转换为字符串即可,to_string(num),因为你显示的是“字符串数字”
}

shuduku.h

#pragma once
#include "common.h"
class Button;
class Sudoku
{
public:
    Sudoku();//数独类
    void DrawMap();//数独图变量
    int ClickButton(ExMessage& msg);//点击函数,鼠标信息
    void MouseMessage(ExMessage& msg);//鼠标信息函数,处理数独类里面的鼠标消息
    bool GameOver();//游戏结束
protected:
    //int map[9][9]
    //array数组可以访问成员函数,所以用array
    array<array<int, 9>, 9> map =
    {
        3,0,6,4,5,1,8,7,9,
        0,4,5,7,0,9,2,0,0,
        7,0,9,2,0,0,1,4,5,
        0,0,3,5,4,7,6,9,0,
        0,0,0,0,9,0,0,1,2,
        6,0,8,0,2,3,4,0,0,
        5,0,1,0,0,2,9,6,4,
        8,6,4,0,0,5,7,2,3,
        9,7,2,3,6,4,0,8,0
    };

    //存放按钮,为每一个按钮配置一个可否修改的标记bool
    vector<pair<bool, Button*>> rect;     //放入函数名指针 Button*,相当于地址,把函数放进去了

};

shuduku.cpp

#include "sudoku.h"
#include "button.h"
Sudoku::Sudoku()
{//地图绘制,按钮创造嵌套循环(二维数组)
	for (int i = 0; i < map.size(); i++) //;行数,size是结构体变量用map.访问,下面push_back是vector库里面的类成员函数,也可以直接用.引用
	{
		for (int j = 0; j < map[i].size(); j++) //列数,内部循环 map[i].size()次,外部循环一次
		{
			int x = WIDTH * j;//格子坐标
			int y = HEIGHT * i;
			if (map[i][j] != 0) //如果里面的数不是0,map可以理解为红黑数
			{
				//不可修改的黑色false,C++万物皆为对象,vector库里面的对象都可以用“.”访问变量
				rect.push_back(make_pair<bool, Button*>(false, new Button(x, y, WIDTH, HEIGHT, LIGHTGRAY, to_string(map[i][j]))));
			}//rect函数用来存储一个矩形框的左上角坐标、宽度和高度,
			//push_back插入将一个新的元素加到vector的最后面,位置为当前最后一个元素的下一个元素
			//当有必要对一个接受pair参数的函数传递两个值时, make_pair()尤其显得方便,pair主要的作用是将两个数据组合成一个数据,两个数据可以是同一类型或者不同类型
			//Button*指针放进去
			else 
			{
				//可修改白色true
				rect.push_back(make_pair<bool, Button*>(true, new Button(x, y, WIDTH, HEIGHT, WHITE, to_string(map[i][j]))));
			}
		}
	}
}

void Sudoku::DrawMap()
{
	for (auto v : rect)
	{
		v.second->Show();
	}
}

int Sudoku::ClickButton(ExMessage& msg)
{
	for (int i = 0; i < rect.size(); i++) //查找位置
	{
		if (rect[i].second->ClickButton(msg) == true)//鼠标点击,返回i这个数,i为当前值
			return i;
	}
	return -1;
}
void Sudoku::MouseMessage(ExMessage& msg)
{
	int pos = ClickButton(msg);//调用点击函数,用pos接受函数,指针接收地址,普通变量接受处地址外的东西,pos=鼠标里面的数字
	if (pos != -1 && rect[pos].first == true && msg.message == WM_LBUTTONUP)//前提是鼠标里面的数字不等于-1,并且当前( rect[pos].first)按钮为白色(true)
	{
		rect[pos].second->SetText();//改变当前按钮文字
	}
}
bool Sudoku::GameOver()
{
	for (int i = 0; i < map.size(); i++) 
	{
		for (int j = 0; j < map[i].size(); j++) 
		{
			//横向判断
			for (int n = 0; n < 9; n++) //只可意会不可言传。。。
			{
				if (n != j && map[i][n] == map[i][j])//n不等于当前列数
					return false;
			}
			//纵向判断
			for (int m = 0; m < 9; m++) 
			{
				if (m != i && map[m][j] == map[i][j])//m不等于当前行数
					return false;
			}
			//九宫格判断。。。。最难的只可意会不可言传
			//每一个九宫格中数字定位到起始位置
			int p = i / 3 * 3;//取整百分百是3的倍数
			int q = j / 3 * 3;
			for (int x = p; x < p + 3; x++) 
			{
				for (int y = q; y < q + 3; y++) 
				{
					if (x != i && j != y && map[x][y] == map[i][j])
						return false;
				}
			}
		}
	}
	return true;
}

main.cpp```

```cpp
#include "widget.h"
#include "sudoku.h"
int main()
{
	Widget w(9 * WIDTH, 9 * HEIGHT);//界面大小540*540
	w.Show();//显示界面
	Sudoku sudoku;//数独库引用
	ExMessage msg;//鼠标变量
	BeginBatchDraw();//稳定屏幕显示,开始双缓冲防止闪屏
	while (1)
	{
		cleardevice();//屏幕刷新显示
		//消息的接受peekmessage函数
		if (peekmessage(&msg, EM_MOUSE))//获取鼠标消息
		{
			sudoku.MouseMessage(msg);
		};

		sudoku.DrawMap();
		if (sudoku.GameOver())//游戏结束了
		{
			break;	请添加图片描述

		}
		cout << "恭喜过关" << endl;
		FlushBatchDraw();//结束双缓冲
	}
	EndBatchDraw();//结束关闭绘画

	return 0;
}

数字居中处理图解
请添加图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小卷同學

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值