C语言如何实现数独程序

这篇文章主要介绍“C语言如何实现数独程序”,在日常操作中,相信很多人在C语言如何实现数独程序问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”C语言如何实现数独程序”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!

程序截图

C语言如何实现数独程序

C语言如何实现数独程序

C语言如何实现数独程序

简单说明

随机生成数独的算法见力扣上对应题目的题解,我用的是递归回溯法

力扣原题

先随机放入 11 个数就能生成一个数独然后求数独的解最后选择要显示的数字再显示出来。这里还用到了洗牌算法选择要随机显示的数字。

代码实现

TCW_GUI.h

 // 程序:数独
// 编译环境:Visual Studio 2019,EasyX_20211109
 
 
#pragma once
#include<graphics.h>
#include<string>
#include<list>
#include<functional>
#include<stdio.h>
#pragma comment( lib, "MSIMG32.LIB")
#define TCW_GUI_BUTTON_MYSELF 0
#ifdef UNICODE
#define STRING std::wstring
#define SPRINTF swprintf_s
#define SSCANF swscanf_s
#else
#define STRING std::string
#define SPRINTF sprintf
#define SSCANF sscanf
#endif // UNICODE
 
namespace TCW_GUI
{
	enum class State
	{
		general = 0,
		touch = 1,
		press = 2,
		release = 3,
		forbidden = 4
	};
	enum class DrawDecision
	{
		text = 0,
		graph = 1
	};
 
	class Vec2
	{
	public:
		double x, y;
		Vec2() :x(0), y(0) {}
		Vec2(double xx, double yy) :x(xx), y(yy) {};
		Vec2 operator+(Vec2 num)
		{
			return Vec2(x + num.x, y + num.y);
		}
		Vec2 operator-(Vec2 num)
		{
			return Vec2(x - num.x, y - num.y);
		}
		Vec2 operator/(double num)
		{
			return Vec2(x / num, y / num);
		}
		Vec2 operator*(double num)
		{
			return Vec2(x * num, y * num);
		}
	};
 
	class Rect
	{
	public:
		Rect() :size(), position() {}
		Rect(Vec2 position, Vec2 size) :size(size), position(position) {}
		Vec2 size;
		Vec2 position;
		bool isInRect(Vec2 point)
		{
			Vec2 left_top = position - size / 2.0;
			Vec2 right_buttom = position + size / 2.0;
			if (point.x >= left_top.x && point.y >= left_top.y &&
				point.x <= right_buttom.x && point.y <= right_buttom.y)return true;
			return false;
		}
	};
 
	class Button
	{
	private:
		double textsize = 20;
		double textareasize = 0.9;
		Vec2 defaultsize;
		Vec2 defaulttext;
		State nowstate = State::general;
		DrawDecision drawdecision = DrawDecision::text;
		void DrawButton_General();
		void DrawButton_Touch();
		void DrawButton_Press();
		void DrawButton_Forbidden();
		bool isPress = false;
	public:
		Button() :boundingbox(), buttontext()
		{
			settextstyle(textsize, 0, TEXT("微软雅黑"));
			Vec2 defaultsize = Vec2(textwidth(TEXT("...")) / textareasize, textheight(TEXT("...")) / textareasize);
			Vec2 defaulttext = Vec2(textwidth(TEXT("...")), textheight(TEXT("...")));
		}
		Button(Rect boundingbox, STRING buttontext, std::function<int(void*)> releaseFunc, void* releaseParam) :
			boundingbox(boundingbox), buttontext(buttontext), releaseFunc(releaseFunc), releaseParam(releaseParam)
		{
			drawdecision = DrawDecision::text;
			settextstyle(textsize, 0, TEXT("微软雅黑"));
			Vec2 defaultsize = Vec2(textwidth(TEXT("...")) / textareasize, textheight(TEXT("...")) / textareasize);
			Vec2 defaulttext = Vec2(textwidth(TEXT("...")), textheight(TEXT("...")));
		}
		Button(Rect boundingbox, STRING graphsrc_normal, STRING graphsrc_touch, STRING graphsrc_press,
			std::function<int(void*)> releaseFunc, void* releaseParam) :
			boundingbox(boundingbox), graphsrc_normal(graphsrc_normal), graphsrc_touch(graphsrc_touch), graphsrc_press(graphsrc_press),
			releaseFunc(releaseFunc), releaseParam(releaseParam)
		{
			drawdecision = DrawDecision::graph;
		}
		STRING buttontext;
		STRING graphsrc_normal;
		STRING graphsrc_touch;
		STRING graphsrc_press;
 
		Rect boundingbox;
		std::function<int(void*)> releaseFunc = nullptr;
		void* releaseParam = nullptr;
		void DrawButton();
		void DrawButton_Text(State state);
		void DrawButton_Graph(State state);
		void receiver(ExMessage* msg);
		void ForbidButton() { this->nowstate = State::forbidden; }	// 禁用按钮
		void RefreshButton() { this->nowstate = State::general; }	// 恢复按钮
		void SetTextSize(double size)
		{
			textsize = size;
			defaultsize = Vec2(textsize * 1.5 / textareasize, textsize / textareasize);
			defaulttext = Vec2(textsize * 1.5, textsize);
		}
		void SetTextAreaSize(double size)
		{
			textareasize = size;
			defaultsize = Vec2(textsize * 1.5 / textareasize, textsize / textareasize);
			defaulttext = Vec2(textsize * 1.5, textsize);
		}
	};
 
	class ButtonManager
	{
		std::list<Button> buttonlist;
	public:
		Button* AddButton(Button button);
		void ReceiveMessage(ExMessage* msg);
		void DrawButton();
	};
 
	void Rectangle_TCW(Vec2 left_top, Vec2 right_buttom)
	{
		rectangle(left_top.x, left_top.y, right_buttom.x, right_buttom.y);
	}
 
	void Fillrectangle_TCW(Vec2 left_top, Vec2 right_buttom)
	{
		fillrectangle(left_top.x, left_top.y, right_buttom.x, right_buttom.y);
	}
 
	void Outtextxy_TCW(Vec2 position, const TCHAR* str)
	{
		outtextxy(position.x, position.y, str);
	}
 
	void Button::DrawButton_Text(State state)
	{
		LOGFONT log;
		COLORREF textcol;
		COLORREF linecol;
		COLORREF fillcol;
		int bkmode;
		gettextstyle(&log);
		bkmode = getbkmode();
		textcol = gettextcolor();
		linecol = getlinecolor();
		fillcol = getfillcolor();
 
		// 默认数值
		settextstyle(textsize, 0, TEXT("微软雅黑"));
		settextcolor(BLACK);
		setbkmode(TRANSPARENT);
		setlinecolor(BLACK);
		setfillcolor(WHITE);
		switch (state)
		{
		case TCW_GUI::State::general:
			break;
		case TCW_GUI::State::touch:
			setfillcolor(RGB(240, 240, 240));
			break;
		case TCW_GUI::State::press:
			setfillcolor(RGB(240, 240, 240));
			break;
		case TCW_GUI::State::release:
			break;
		case TCW_GUI::State::forbidden:
			settextcolor(RGB(128, 128, 128));
			break;
		default:
			break;
		}
		Vec2 size_button = Vec2(this->boundingbox.size * textareasize);
		Vec2 size_text = Vec2(textwidth(this->buttontext.c_str()), textsize);
 
		if (boundingbox.size.x > defaultsize.x && boundingbox.size.y > defaultsize.y)	// 比最小值大
		{
			Rectangle_TCW(this->boundingbox.position - this->boundingbox.size / 2.0,
				this->boundingbox.position + this->boundingbox.size / 2.0);
			Fillrectangle_TCW(this->boundingbox.position - this->boundingbox.size / 2.0,
				this->boundingbox.position + this->boundingbox.size / 2.0);
			if (size_button.x >= size_text.x && size_button.y >= size_text.y)	// 字数没超
			{
				switch (state)
				{
				case TCW_GUI::State::general:
					Outtextxy_TCW(this->boundingbox.position - size_text / 2.0, buttontext.c_str());
					break;
				case TCW_GUI::State::touch:
					Outtextxy_TCW(this->boundingbox.position - size_text / 2.0, buttontext.c_str());
					break;
				case TCW_GUI::State::press:
					Outtextxy_TCW(this->boundingbox.position - size_text / 2.0 + Vec2(2, 2), buttontext.c_str());
					break;
				case TCW_GUI::State::release:
					break;
				case TCW_GUI::State::forbidden:
					Outtextxy_TCW(this->boundingbox.position - size_text / 2.0, buttontext.c_str());
					break;
				default:
					break;
				}
			}
			else	// 字数超了
			{
				int wordnum = size_button.x / textwidth(buttontext.c_str()) * buttontext.size() - 2;
				STRING realstr = buttontext.substr(0, wordnum);
				realstr += TEXT("...");
				Outtextxy_TCW(this->boundingbox.position - size_text / 2.0, realstr.c_str());
				switch (state)
				{
				case TCW_GUI::State::general:
					Outtextxy_TCW(this->boundingbox.position - size_text / 2.0, realstr.c_str());
					break;
				case TCW_GUI::State::touch:
					Outtextxy_TCW(this->boundingbox.position - size_text / 2.0, realstr.c_str());
					break;
				case TCW_GUI::State::press:
					Outtextxy_TCW(this->boundingbox.position - size_text / 2.0 + Vec2(2, 2), realstr.c_str());
					break;
				case TCW_GUI::State::release:
					break;
				case TCW_GUI::State::forbidden:
					Outtextxy_TCW(this->boundingbox.position - size_text / 2.0, realstr.c_str());
					break;
				default:
					break;
				}
			}
		}
		else	// 比最小值小
		{
			Rectangle_TCW(this->boundingbox.position - this->defaultsize / 2.0,
				this->boundingbox.position + this->defaultsize / 2.0);
			Fillrectangle_TCW(this->boundingbox.position - this->defaultsize / 2.0,
				this->boundingbox.position + this->defaultsize / 2.0);
			if (defaulttext.x >= size_text.x && defaulttext.y >= size_text.y)	// 字宽比三个点小
			{
				switch (state)
				{
				case TCW_GUI::State::general:
					Outtextxy_TCW(this->boundingbox.position - size_text / 2.0, buttontext.c_str());
					break;
				case TCW_GUI::State::touch:
					Outtextxy_TCW(this->boundingbox.position - size_text / 2.0, buttontext.c_str());
					break;
				case TCW_GUI::State::press:
					Outtextxy_TCW(this->boundingbox.position - size_text / 2.0 + Vec2(2, 2), buttontext.c_str());
					break;
				case TCW_GUI::State::release:
					break;
				case TCW_GUI::State::forbidden:
					Outtextxy_TCW(this->boundingbox.position - size_text / 2.0, buttontext.c_str());
					break;
				default:
					break;
				}
			}
			else	// 字宽比三个点大
			{
				switch (state)
				{
				case TCW_GUI::State::general:
					Outtextxy_TCW(this->boundingbox.position - defaulttext / 2.0, TEXT("..."));
					break;
				case TCW_GUI::State::touch:
					Outtextxy_TCW(this->boundingbox.position - defaulttext / 2.0, TEXT("..."));
					break;
				case TCW_GUI::State::press:
					Outtextxy_TCW(this->boundingbox.position - size_text / 2.0 + Vec2(2, 2), TEXT("..."));
					break;
				case TCW_GUI::State::release:
					break;
				case TCW_GUI::State::forbidden:
					Outtextxy_TCW(this->boundingbox.position - defaulttext / 2.0, TEXT("..."));
					break;
				default:
					break;
				}
			}
		}
 
		settextstyle(&log);
		settextcolor(textcol);
		setbkmode(bkmode);
		setlinecolor(linecol);
		setfillcolor(fillcol);
	}
 
	void Button::DrawButton_Graph(State state)
	{
		IMAGE img;
		switch (state)
		{
		case TCW_GUI::State::general:
			loadimage(&img, (this->graphsrc_normal).c_str());
			break;
		case TCW_GUI::State::touch:
			loadimage(&img, (this->graphsrc_touch).c_str());
			break;
		case TCW_GUI::State::press:
			loadimage(&img, (this->graphsrc_press).c_str());
			break;
		case TCW_GUI::State::release:
			break;
		case TCW_GUI::State::forbidden:
			loadimage(&img, (this->graphsrc_normal).c_str());
			break;
		default:
			break;
		}
		IMAGE* srcimg = &img;
		HDC dstDC = GetImageHDC(NULL);
		HDC srcDC = GetImageHDC(srcimg);
		int w = srcimg->getwidth();
		int h = srcimg->getheight();
 
		// 结构体的第三个成员表示额外的透明度,0 表示全透明,255 表示不透明。
		BLENDFUNCTION bf = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA };
		// 使用 Windows GDI 函数实现半透明位图
		Vec2 left_top = this->boundingbox.position - this->boundingbox.size / 2.0;
		AlphaBlend(dstDC, left_top.x, left_top.y, boundingbox.size.x, boundingbox.size.y, srcDC, 0, 0, w, h, bf);
	}
 
	void Button::DrawButton_General()
	{
		switch (this->drawdecision)
		{
		case TCW_GUI::DrawDecision::text:DrawButton_Text(State::general);
			break;
		case TCW_GUI::DrawDecision::graph:DrawButton_Graph(State::general);
			break;
		default:
			break;
		}
	}
 
	void Button::DrawButton_Touch()
	{
		switch (this->drawdecision)
		{
		case TCW_GUI::DrawDecision::text:DrawButton_Text(State::touch);
			break;
		case TCW_GUI::DrawDecision::graph:DrawButton_Graph(State::touch);
			break;
		default:
			break;
		}
	}
 
	void Button::DrawButton_Press()
	{
		switch (this->drawdecision)
		{
		case TCW_GUI::DrawDecision::text:DrawButton_Text(State::press);
			break;
		case TCW_GUI::DrawDecision::graph:DrawButton_Graph(State::press);
			break;
		default:
			break;
		}
	}
 
	void Button::DrawButton_Forbidden()
	{
		switch (drawdecision)
		{
		case TCW_GUI::DrawDecision::text:DrawButton_Text(State::forbidden);
			break;
		case TCW_GUI::DrawDecision::graph:DrawButton_Graph(State::forbidden);
			break;
		default:
			break;
		}
	}
 
	void Button::DrawButton()
	{
		switch (this->nowstate)
		{
		case State::general:
			DrawButton_General();
			break;
		case State::touch:
			DrawButton_Touch();
			break;
		case State::press:
			DrawButton_Press();
			break;
		case State::release:
			DrawButton_Touch();
			if (releaseFunc != nullptr)
			{
				if (releaseParam == TCW_GUI_BUTTON_MYSELF)releaseFunc(this);
				else releaseFunc(releaseParam);
			}
			this->nowstate = State::touch;
			break;
		case State::forbidden:
			DrawButton_Forbidden();
			break;
		default:
			break;
		}
	}
 
	void Button::receiver(ExMessage* msg)
	{
		if (this->nowstate == State::forbidden)return;
		// 先 general 后 touch 再 press 一个 release 后重新 general
		if (!isPress && !this->boundingbox.isInRect(Vec2(msg->x, msg->y)))
		{
			this->nowstate = State::general;
		}
		else if (!isPress && this->boundingbox.isInRect(Vec2(msg->x, msg->y)))
		{
			if (!msg->lbutton)
				this->nowstate = State::touch;
			else if (this->nowstate == State::touch)
			{
				isPress = true;
				this->nowstate = State::press;
			}
		}
		else if (isPress && this->boundingbox.isInRect(Vec2(msg->x, msg->y)))
		{
			if (!msg->lbutton)
			{
				isPress = false;
				this->nowstate = State::release;
			}
			else this->nowstate = State::press;
		}
		else if (isPress && !this->boundingbox.isInRect(Vec2(msg->x, msg->y)))
		{
			if (!msg->lbutton)
			{
				isPress = false;
				this->nowstate = State::general;
			}
			else this->nowstate = State::press;
		}
	}
 
	Button* ButtonManager::AddButton(Button button)
	{
		this->buttonlist.push_back(button);
		return &buttonlist.back();
	}
 
	void ButtonManager::ReceiveMessage(ExMessage* msg)
	{
		for (Button& button : this->buttonlist)
		{
			button.receiver(msg);
		}
	}
 
	void ButtonManager::DrawButton()
	{
		for (Button& button : this->buttonlist)
		{
			button.DrawButton();
		}
	}
}

main.cpp

// 程序:C语言数独
// 编译环境:Visual Studio 2019,EasyX_20211109
 
#include"TCW_GUI.h"
#include<time.h>
#define WIDTH 640
#define HEIGHT 480
 
class Vec2
{
public:
	double xx, yy;
	Vec2(double xx = 0, double yy = 0) :xx(xx), yy(yy) {}
	Vec2 operator+(Vec2 ano)
	{
		return Vec2(xx + ano.xx, yy + ano.yy);
	}
	Vec2 operator-(Vec2 ano)
	{
		return Vec2(xx - ano.xx, yy - ano.yy);
	}
	Vec2 operator/(double num)
	{
		return Vec2(xx / num, yy / num);
	}
};
 
class BoundingBox
{
public:
	Vec2 size, position;
	BoundingBox(Vec2 size, Vec2 position) :size(size), position(position) {}
	BoundingBox() = default;
	Vec2 GetLeftTop()
	{
		return position - size / 2.0;
	}
	bool isInBoundingBox(Vec2 point)
	{
		return (point.xx > position.xx - size.xx / 2.0 && point.xx < position.xx + size.xx / 2.0 &&
			point.yy>position.yy - size.yy / 2.0 && point.yy < position.yy + size.yy / 2.0);
	}
};
 
 
template<typename T>
void WashCard(T* map, int len)
{
	for (int i = len; i > 0; i--)
	{
		int exchange = rand() % i;
		T temp = map[i - 1];
		map[i - 1] = map[exchange];
		map[exchange] = temp;
	}
}
 
bool isvalid(char(* const map)[9][9], char c, int row_i, int col_i)
{
	// 尚未放入,判断能否放入
	for (int i = 0; i < 9; ++i)
	{
		if (abs((*map)[row_i][i]) == c) return false;
		else if (abs((*map)[i][col_i]) == c) return false;
		else if (abs((*map)[3 * (row_i / 3) + i / 3][3 * (col_i / 3) + i % 3]) == c) return false;
	}
	return true;
}
 
// 递归回溯算法 yyds
bool solver(char(* const map)[9][9], int rstart)
{
	for (int row = rstart; row < 9; ++row)
	{
		for (int col = 0; col < 9; ++col)
		{
			if ((*map)[row][col] == 0)
			{
				for (char c = 1; c <= 9; ++c)
				{
					if (isvalid(map, c, row, col))
					{
						(*map)[row][col] = c;
						if (solver(map, row)) return true;
						else (*map)[row][col] = 0;
					}
				}
				return false;
			}
		}
	}
	return true;
}
 
void GenerateSudoku(char(* const map)[9][9])
{
	memset(*map, 0, sizeof(*map));
	char arr[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	WashCard(arr, 9);
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			(*map)[i][j] = arr[i * 3 + j];
		}
	}
	WashCard(arr, 9);
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			(*map)[3 + i][3 + j] = arr[i * 3 + j];
		}
	}
	WashCard(arr, 9);
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			(*map)[6 + i][6 + j] = arr[i * 3 + j];
		}
	}
	solver(map, 0);
}
 
void GetShowMap(const char(*map)[9][9], char(* const ShowMap)[9][9], bool(* const Auxiliary)[9][9], int showNum)
{
	memset(*Auxiliary, 0, sizeof(bool) * 81);
	memset(*ShowMap, 0, sizeof(char) * 81);
	char arr[81];
	for (int i = 0; i < 81; i++)arr[i] = i;
	WashCard(arr, 81);
	for (int i = 0; i < showNum; i++)
	{
		(*ShowMap)[arr[i] / 9][arr[i] % 9] = (*map)[arr[i] / 9][arr[i] % 9];
		(*Auxiliary)[arr[i] / 9][arr[i] % 9] = true;
	}
}
 
void Rectangle_MR(Vec2 left_top, Vec2 right_bottom)
{
	rectangle((int)(left_top.xx + 0.5), (int)(left_top.yy + 0.5), (int)(right_bottom.xx + 0.5), (int)(right_bottom.yy + 0.5));
}
 
void Fillrectangle_MF(Vec2 left_top, Vec2 right_bottom)
{
	fillrectangle((int)(left_top.xx + 0.5), (int)(left_top.yy + 0.5), (int)(right_bottom.xx + 0.5), (int)(right_bottom.yy + 0.5));
}
 
void Outtextxy_MO(Vec2 pericenter, LPCTSTR str)
{
	int width = textwidth(str);
	int height = textheight(str);
	outtextxy((int)(pericenter.xx - width / 2.0 + 0.5), (int)(pericenter.yy - height / 2.0 + 0.5), str);
}
 
void DrawMap(const char(*showMap)[9][9], const bool(*Auxiliary)[9][9], bool isSelected, int indexX, int indexY, BoundingBox boundingbox)
{
	Vec2 grid = Vec2(boundingbox.size.xx / 9.0, boundingbox.size.yy / 9.0);
	Vec2 beginPoint = boundingbox.GetLeftTop();
	settextstyle(grid.yy * 0.8, 0, TEXT("consolas"));
	settextcolor(BLACK);
	setlinestyle(PS_SOLID, 2);
	setlinecolor(BLACK);
	setfillcolor(WHITE);
	setbkmode(TRANSPARENT);
	for (int i = 0; i < 9; i++)
	{
		for (int j = 0; j < 9; j++)
		{
			if (isSelected && i == indexY && j == indexX)setfillcolor(RED);
			else if ((*Auxiliary)[i][j])setfillcolor(WHITE);
			else setfillcolor(RGB(127, 127, 127));
			Fillrectangle_MF(beginPoint + Vec2(j * grid.xx, i * grid.yy), beginPoint + Vec2((j + 1) * grid.xx, (i + 1) * grid.yy));
			if ((*showMap)[i][j] != 0)
			{
				TCHAR arr[16];
				if ((*showMap)[i][j] < 0)settextcolor(LIGHTRED);
				else settextcolor(BLACK);
				SPRINTF(arr, TEXT("%d"), abs((*showMap)[i][j]));
				Outtextxy_MO(beginPoint + Vec2((j + 0.5) * grid.xx, (i + 0.5) * grid.yy), arr);
			}
		}
	}
	setlinestyle(PS_SOLID, 3);
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 3; j++)
		{
			Rectangle_MR(beginPoint + Vec2(j * 3 * grid.xx, i * 3 * grid.yy), beginPoint + Vec2((j + 1) * 3 * grid.xx, (i + 1) * 3 * grid.yy));
		}
	}
}
 
bool DetectedSudoku(const char(*map)[9][9])
{
	bool nightHash[9][9] = { false };
	bool rowHash[9][9] = { false };
	bool colHash[9][9] = { false };
	for (int i = 0; i < 9; i++)
	{
		for (int j = 0; j < 9; j++)
		{
			if ((*map)[i][j] == 0)continue;
			else if ((*map)[i][j] < 0)return false;
			if (nightHash[i / 3 * 3 + j / 3][(*map)[i][j] - 1] == true)return false;
			else nightHash[i / 3 * 3 + j / 3][(*map)[i][j] - 1] = true;
			if (rowHash[i][(*map)[i][j] - 1] == true)return false;
			else rowHash[i][(*map)[i][j] - 1] = true;
			if (colHash[j][(*map)[i][j] - 1] == true)return false;
			else colHash[j][(*map)[i][j] - 1] = true;
		}
	}
	return true;
}
 
void StartScene();
void RandomScene();
void CustomMode();
 
 
int main()
{
	initgraph(WIDTH, HEIGHT);
	BeginBatchDraw();
	srand((unsigned int)time(NULL));
	StartScene();
	closegraph();
	return 0;
}
 
void StartScene()
{
	cleardevice();
	ExMessage msg;
	bool isExit = false;
 
	TCW_GUI::ButtonManager manager;
	double buttonheight = (HEIGHT * 3 / 4.0 - 40) / 3.0;
	double buttonwidth = WIDTH / 3;
	manager.AddButton(TCW_GUI::Button(TCW_GUI::Rect(TCW_GUI::Vec2(WIDTH / 2, HEIGHT / 4 + 10 + buttonheight / 2),
		TCW_GUI::Vec2(buttonwidth, 50)), TEXT("自定义模式"), [](void*) {CustomMode(); return 0; }, nullptr));
	manager.AddButton(TCW_GUI::Button(TCW_GUI::Rect(TCW_GUI::Vec2(WIDTH / 2, HEIGHT / 4 + 20 + buttonheight * 1.5),
		TCW_GUI::Vec2(buttonwidth, 50)), TEXT("随机模式"), [](void*) { RandomScene(); return 0; }, nullptr));
	manager.AddButton(TCW_GUI::Button(TCW_GUI::Rect(TCW_GUI::Vec2(WIDTH / 2, HEIGHT / 4 + 40 + buttonheight * 2.5),
		TCW_GUI::Vec2(buttonwidth, 50)), TEXT("退出游戏"), [](void* param) {*(bool*)param = true; return 0; }, &isExit));
	Vec2 pericenter(WIDTH / 2, HEIGHT / 8);
	while (!isExit)
	{
		if (peekmessage(&msg, EM_MOUSE))
		{
			manager.ReceiveMessage(&msg);
			manager.DrawButton();
		}
		settextstyle((int)(HEIGHT / 4.0 * 0.8 + 0.5), 0, TEXT("楷体"));
		settextcolor(WHITE);
		Outtextxy_MO(pericenter, TEXT("数独"));
		FlushBatchDraw();
	}
	cleardevice();
}
 
void RandomScene()
{
	cleardevice();
	char map[9][9];
	memset(map, 0, sizeof(map));
	GenerateSudoku(&map);
	char showMap[9][9] = { 0 };
	bool Auxiliary[9][9] = { false };
	int CountOfNum = 27;
	int CanFillNum = 81 - CountOfNum;
	GetShowMap(&map, &showMap, &Auxiliary, CountOfNum);
 
	ExMessage msg;
	bool isExit = false;
	bool isSelected = false;
	bool isWin = false;
	int indexX, indexY;
 
	TCW_GUI::ButtonManager command;
	command.AddButton(TCW_GUI::Button(TCW_GUI::Rect(TCW_GUI::Vec2(60, 50),
		TCW_GUI::Vec2(100, 40)), TEXT("返回"), [](void* param) {*(bool*)param = true; return 0; }, &isExit));
	command.AddButton(TCW_GUI::Button(TCW_GUI::Rect(TCW_GUI::Vec2(60, 100),
		TCW_GUI::Vec2(100, 40)), TEXT("设置数字数量"), [](void* param)
		{
			TCHAR temp[3];
			SPRINTF(temp, TEXT("%d"), *(int*)param);
			InputBox(temp, 3, TEXT("请输入要设置的数字数量:"), TEXT("输入框"), temp);
			int tempNum;
			SSCANF(temp, TEXT("%d"), &tempNum);
			if (tempNum >= 17 && tempNum < 81)*(int*)param = tempNum;
			return 0;
		}, &CountOfNum));
	command.AddButton(TCW_GUI::Button(TCW_GUI::Rect(TCW_GUI::Vec2(60, 150),
		TCW_GUI::Vec2(100, 40)), TEXT("重置"), [&](void* param)
		{
			GenerateSudoku(&map);
			GetShowMap(&map, &showMap, &Auxiliary, CountOfNum);
			CanFillNum = 81 - CountOfNum;
			isWin = false;
			return 0;
		}, nullptr));
	TCW_GUI::ButtonManager manager;
	BoundingBox Rect(Vec2(300, 300), Vec2(WIDTH / 2.0, HEIGHT / 2.0));
	char Answer[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	for (int i = 0; i < 9; i++)
	{
		TCHAR arr[16];
		SPRINTF(arr, TEXT("%d"), i + 1);
		manager.AddButton(TCW_GUI::Button(TCW_GUI::Rect(TCW_GUI::Vec2(WIDTH - HEIGHT / 10, HEIGHT / 10 * (i + 1)),
			TCW_GUI::Vec2(HEIGHT / 11, HEIGHT / 11)), arr, [&](void* param)
			{
				if (isSelected)
				{
					// 检查是否正确
					if (showMap[indexY][indexX] > 0)CanFillNum++;
					showMap[indexY][indexX] = 0;
					char tempNum = *(char*)param;
					if (!isvalid(&showMap, tempNum, indexY, indexX))tempNum = -tempNum;
					else CanFillNum--;
					showMap[indexY][indexX] = tempNum;
					isSelected = false;
					if (CanFillNum == 0)
					{
						isWin = true;
					}
					else isWin = false;
				}
				return 0;
			}, Answer + i));
	}
 
	while (!isExit)
	{
		if (peekmessage(&msg, EM_MOUSE))
		{
			command.ReceiveMessage(&msg);
			manager.ReceiveMessage(&msg);
			if (msg.lbutton && Rect.isInBoundingBox(Vec2(msg.x, msg.y)))
			{
				Vec2 direct = Vec2(msg.x, msg.y) - Rect.GetLeftTop();
				Vec2 Grid = Rect.size / 9.0;
				indexX = direct.xx / Grid.xx;
				indexY = direct.yy / Grid.yy;
				if (Auxiliary[indexY][indexX] == false)isSelected = true;
				else isSelected = false;
			}
			else if (msg.rbutton && Rect.isInBoundingBox(Vec2(msg.x, msg.y)))
			{
				Vec2 direct = Vec2(msg.x, msg.y) - Rect.GetLeftTop();
				Vec2 Grid = Rect.size / 9.0;
				indexX = direct.xx / Grid.xx;
				indexY = direct.yy / Grid.yy;
				if (Auxiliary[indexY][indexX] == false)
				{
					if (showMap[indexY][indexX] > 0)
					{
						CanFillNum++;
						isWin = false;
					}
					showMap[indexY][indexX] = 0;
				}
			}
		}
		cleardevice();
		manager.DrawButton();
		command.DrawButton();
		DrawMap(&showMap, &Auxiliary, isSelected, indexX, indexY, Rect);
		if (isWin)
		{
			settextcolor(YELLOW);
			Outtextxy_MO(Vec2(WIDTH / 2, 50), TEXT("恭喜你,胜利了"));
		}
		FlushBatchDraw();
	}
	cleardevice();
}
 
void CustomMode()
{
	cleardevice();
	char map[9][9] = { 0 };
	char showMap[9][9] = { 0 };
	bool Auxiliary[9][9] = { false };
	int CanFillNum = 81;
 
	ExMessage msg;
	bool isExit = false;
	bool isSelected = false;
	bool isWin = false;
	int indexX, indexY;
	bool isGamePlay = false;
 
	TCW_GUI::ButtonManager command;
	command.AddButton(TCW_GUI::Button(TCW_GUI::Rect(TCW_GUI::Vec2(90, 50),
		TCW_GUI::Vec2(150, 40)), TEXT("返回"), [](void* param) {*(bool*)param = true; return 0; }, &isExit));
	command.AddButton(TCW_GUI::Button(TCW_GUI::Rect(TCW_GUI::Vec2(90, 100),
		TCW_GUI::Vec2(150, 40)), TEXT("开始解数独"), [&](void* param)
		{
			// 判断是否安全忘了!!!
			if (!DetectedSudoku(&map))
			{
				settextcolor(LIGHTGRAY);
				Outtextxy_MO(Vec2(WIDTH / 2, HEIGHT / 2), TEXT("这个数独不成立"));
				FlushBatchDraw();
				Sleep(500);
				return 0;
			}
			else
			{
				memcpy(showMap, map, sizeof(map));
				if (!solver(&showMap, 0))
				{
					settextcolor(LIGHTGRAY);
					Outtextxy_MO(Vec2(WIDTH / 2, HEIGHT / 2), TEXT("这个数独无解"));
					FlushBatchDraw();
					Sleep(500);
					return 0;
				}
			}
			isGamePlay = true;
			for (int i = 0; i < 9; i++)
			{
				for (int j = 0; j < 9; j++)
				{
					if (map[i][j] != 0)Auxiliary[i][j] = true;
					showMap[i][j] = map[i][j];
				}
			}
			return 0;
		}, nullptr));
	command.AddButton(TCW_GUI::Button(TCW_GUI::Rect(TCW_GUI::Vec2(90, 150),
		TCW_GUI::Vec2(150, 40)), TEXT("重置"), [&](void* param)
		{
			memset(Auxiliary, 0, sizeof(Auxiliary));
			memset(showMap, 0, sizeof(showMap));
			memset(map, 0, sizeof(map));
			CanFillNum = 81;
			isWin = false;
			isGamePlay = false;
			return 0;
		}, nullptr));
	command.AddButton(TCW_GUI::Button(TCW_GUI::Rect(TCW_GUI::Vec2(90, 200),
		TCW_GUI::Vec2(150, 40)), TEXT("展示其中一种答案"), [&](void* param)
		{
			memcpy(showMap, map, sizeof(map));
			if (!solver(&showMap, 0))
			{
				settextcolor(LIGHTGRAY);
				Outtextxy_MO(Vec2(WIDTH / 2, HEIGHT / 2), TEXT("这个数独无解"));
				FlushBatchDraw();
				Sleep(500);
			}
			else
			{
				memset(Auxiliary, true, sizeof(Auxiliary));
				CanFillNum = 0;
				isWin = false;
				isGamePlay = true;
			}
			return 0;
		}, nullptr));
	TCW_GUI::ButtonManager manager;
	BoundingBox Rect(Vec2(300, 300), Vec2(WIDTH / 2.0, HEIGHT / 2.0));
	char Answer[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
	for (int i = 0; i < 9; i++)
	{
		TCHAR arr[16];
		SPRINTF(arr, TEXT("%d"), i + 1);
		manager.AddButton(TCW_GUI::Button(TCW_GUI::Rect(TCW_GUI::Vec2(WIDTH - HEIGHT / 10, HEIGHT / 10 * (i + 1)),
			TCW_GUI::Vec2(HEIGHT / 11, HEIGHT / 11)), arr, [&](void* param)
			{
				if (isSelected)
				{
					if (!isGamePlay)
					{
						if (map[indexY][indexX] > 0)CanFillNum++;
						map[indexY][indexX] = 0;
						char tempNum = *(char*)param;
						if (!isvalid(&map, tempNum, indexY, indexX))tempNum = -tempNum;
						else CanFillNum--;
						map[indexY][indexX] = tempNum;
						isSelected = false;
					}
					else
					{
						// 检查是否正确
						if (showMap[indexY][indexX] > 0)CanFillNum++;
						showMap[indexY][indexX] = 0;
						char tempNum = *(char*)param;
						if (!isvalid(&showMap, tempNum, indexY, indexX))tempNum = -tempNum;
						else CanFillNum--;
						showMap[indexY][indexX] = tempNum;
						isSelected = false;
						if (CanFillNum == 0)
						{
							isWin = true;
						}
						else isWin = false;
					}
				}
				return 0;
			}, Answer + i));
	}
 
	while (!isExit)
	{
		if (peekmessage(&msg, EM_MOUSE))
		{
			command.ReceiveMessage(&msg);
			manager.ReceiveMessage(&msg);
			if (msg.lbutton && Rect.isInBoundingBox(Vec2(msg.x, msg.y)))
			{
				Vec2 direct = Vec2(msg.x, msg.y) - Rect.GetLeftTop();
				Vec2 Grid = Rect.size / 9.0;
				indexX = direct.xx / Grid.xx;
				indexY = direct.yy / Grid.yy;
				if (Auxiliary[indexY][indexX] == false)isSelected = true;
				else isSelected = false;
			}
			else if (msg.rbutton && Rect.isInBoundingBox(Vec2(msg.x, msg.y)))
			{
				Vec2 direct = Vec2(msg.x, msg.y) - Rect.GetLeftTop();
				Vec2 Grid = Rect.size / 9.0;
				indexX = direct.xx / Grid.xx;
				indexY = direct.yy / Grid.yy;
				if (Auxiliary[indexY][indexX] == false)
				{
					if (isGamePlay)
					{
						if (showMap[indexY][indexX] > 0)
						{
							CanFillNum++;
							isWin = false;
						}
						showMap[indexY][indexX] = 0;
					}
					else
					{
						if (map[indexY][indexX] > 0)CanFillNum++;
						map[indexY][indexX] = 0;
					}
				}
			}
		}
		cleardevice();
		manager.DrawButton();
		command.DrawButton();
		if (!isGamePlay)DrawMap(&map, &Auxiliary, isSelected, indexX, indexY, Rect);
		else DrawMap(&showMap, &Auxiliary, isSelected, indexX, indexY, Rect);
		if (isWin)
		{
			settextcolor(YELLOW);
			Outtextxy_MO(Vec2(WIDTH / 2, 50), TEXT("恭喜你,胜利了"));
		}
		FlushBatchDraw();
	}
	cleardevice();
}

到此,关于“C语言如何实现数独程序”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程之家网站,小编会继续努力为大家带来更多实用的文章!

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


一.C语言中的static关键字 在C语言中,static可以用来修饰局部变量,全局变量以及函数。在不同的情况下static的作用不尽相同。 (1)修饰局部变量 一般情况下,对于局部变量是存放在栈区的,并且局部变量的生命周期在该语句块执行结束时便结束了。但是如果用static进行修饰的话,该变量便存
浅谈C/C++中的指针和数组(二) 前面已经讨论了指针和数组的一些区别,然而在某些情况下,指针和数组是等同的,下面讨论一下什么时候指针和数组是相同的。C语言标准对此作了说明:规则1:表达式中的数组名被编译器当做一个指向该数组第一个元素的指针; 注:下面几种情况例外 1)数组名作为sizeof的操作数
浅谈C/C++中的指针和数组(一)指针是C/C++的精华,而指针和数组又是一对欢喜冤家,很多时候我们并不能很好的区分指针和数组,对于刚毕业的计算机系的本科生很少有人能够熟练掌握指针以及数组的用法和区别。造成这种原因可能跟现在大学教学以及现在市面上流行的很多C或者C++教程有关,这些教程虽然通俗易懂,
从两个例子分析C语言的声明 在读《C专家编程》一书的第三章时,书中谈到C语言的声明问题,《C专家编程》这本书只有两百多页,却花了一章的内容去阐述这个问题,足以看出这个问题的重要性,要想透彻理解C语言的声明问题仅仅看书是远远不够的,需要平时多实践并大量阅读别人写的代码。下面借鉴《C专家编程》书中的两个
C语言文件操作解析(一)在讨论C语言文件操作之前,先了解一下与文件相关的东西。一.文本文件和二进制文件 文本文件的定义:由若干行字符构成的计算机文件,存在于计算机系统中。文本文件只能存储文件中的有效字符信息,不能存储图像、声音等信息。狭义上的二进制文件则指除开文本文件之外的文件,如图片、DOC文档。
C语言文件操作解析(三) 在前面已经讨论了文件打开操作,下面说一下文件的读写操作。文件的读写操作主要有4种,字符读写、字符串读写、块读写以及格式化读写。一.字符读写 字符读写主要使用两个函数fputc和fgetc,两个函数的原型是: int fputc(int ch,FILE *fp);若写入成功则
浅谈C语言中的位段 位段(bit-field)是以位为单位来定义结构体(或联合体)中的成员变量所占的空间。含有位段的结构体(联合体)称为位段结构。采用位段结构既能够节省空间,又方便于操作。 位段的定义格式为: type [var]:digits 其中type只能为int,unsigned int,s
C语言文件操作解析(五)之EOF解析 在C语言中,有个符号大家都应该很熟悉,那就是EOF(End of File),即文件结束符。但是很多时候对这个理解并不是很清楚,导致在写代码的时候经常出错,特别是在判断文件是否到达文件末尾时,常常出错。1.EOF是什么? 在VC中查看EOF的定义可知: #def
关于VC+ʶ.0中getline函数的一个bug 最近在调试程序时,发现getline函数在VC+ʶ.0和其他编译器上运行结果不一样,比如有如下这段程序:#include &lt;iostream&gt;#include &lt;string&gt;using namespace std;int
C/C++浮点数在内存中的存储方式 任何数据在内存中都是以二进制的形式存储的,例如一个short型数据1156,其二进制表示形式为00000100 10000100。则在Intel CPU架构的系统中,存放方式为 10000100(低地址单元) 00000100(高地址单元),因为Intel CPU
浅析C/C++中的switch/case陷阱 先看下面一段代码: 文件main.cpp#includeusing namespace std;int main(int argc, char *argv[]){ int a =0; switch(a) { case ...
浅谈C/C++中的typedef和#define 在C/C++中,我们平时写程序可能经常会用到typedef关键字和#define宏定义命令,在某些情况下使用它们会达到相同的效果,但是它们是有实质性的区别,一个是C/C++的关键字,一个是C/C++的宏定义命令,typedef用来为一个已有的数据类型
看下面一道面试题:#include&lt;stdio.h&gt;#include&lt;stdlib.h&gt;int main(void) { int a[5]={1,2,3,4,5}; int *ptr=(int *)(&amp;aʱ); printf(&quot;%d,%d&quot;,*(
联合体union 当多个数据需要共享内存或者多个数据每次只取其一时,可以利用联合体(union)。在C Programming Language 一书中对于联合体是这么描述的: 1)联合体是一个结构; 2)它的所有成员相对于基地址的偏移量都为0; 3)此结构空间要大到足够容纳最&quot;宽&quo
从一个程序的Bug解析C语言的类型转换 先看下面一段程序,这段程序摘自《C 专家编程》:#include&lt;stdio.h&gt;int array[]={23,34,12,17,204,99,16};#define TOTAL_ELEMENTS (sizeof(array)/sizeof(ar
大端和小端 嵌入式开发者应该对大端和小端很熟悉。在内存单元中数据是以字节为存储单位的,对于多字节数据,在小端模式中,低字节数据存放在低地址单元,而在大端模式中,低字节数据存放在高地址单元。比如一个定义一个short型的变量a,赋值为1,由于short型数据占2字节。在小端模式中,其存放方式为0X40
位运算和sizeof运算符 C语言中提供了一些运算符可以直接操作整数的位,称为位运算,因此位运算中的操作数都必须是整型的。位运算的效率是比较高的,而且位运算运用好的话会达到意想不到的效果。位运算主要有6种:与(&amp;),或(|),取反(~),异或(^),左移(&gt;)。1.位运算中的类型转换位
C语言文件操作解析(四)在文件操作中除了打开操作以及读写操作,还有几种比较常见的操作。下面介绍一下这些操作中涉及到的函数。一.移动位置指针的函数 rewind函数和fseek函数,这两个函数的原型是:void rewind(FILE *fp); 将位置指针移动到文件首 int fseek(FILE
结构体字节对齐 在用sizeof运算符求算某结构体所占空间时,并不是简单地将结构体中所有元素各自占的空间相加,这里涉及到内存字节对齐的问题。从理论上讲,对于任何变量的访问都可以从任何地址开始访问,但是事实上不是如此,实际上访问特定类型的变量只能在特定的地址访问,这就需要各个变量在空间上按一定的规则排
C语言文件操作解析(二)C语言中对文件进行操作必须首先打开文件,打开文件主要涉及到fopen函数。fopen函数的原型为 FILE* fopen(const char *path,const char *mode) 其中path为文件路径,mode为打开方式 1)对于文件路径,只需注意若未明确给出绝