目录
- 程序截图
- 简单说明
- 代码实现
程序截图
简单说明
随机生成数独的算法见力扣上对应题目的题解,我用的是递归回溯法
先随机放入 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 | |
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(); | |
} |