C语言实现数独辅助器(附源码)
数独游戏介绍
数独是源自瑞士的一种数学游戏。是一种运用纸、笔进行演算的逻辑游戏。玩家需要根据 9×9 盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个粗线宫(3*3)内的数字均含 1-9,不重复 。
数独盘面是个九宫,每一宫又分为九个小格。在这八十一格中给出一定的已知数字和解题条件,利用逻辑和推理,在其他的空格上填入1-9的数字。使1-9每个数字在每一行、每一列和每一宫中都只出现一次,所以又称“九宫格”。
数独辅助器编写思路
首先,肯定是画九宫格,做好这个程序的界面。然后给这个界面的相应位置赋予对应相应的数,用鼠标给这个数独九宫格进行填数。当然做好前面的这些只是表面的,最主要的是如何用电脑来解这个数独呢?我一直在思考,程序其实就是一个工具,而我们就是要学会应用这个工具去做一些我们很难做到的事,编写程序就是一个解决问题的好办法。我记得我曾经花了一周的时间去解一个数独,虽然数独最终解出来了,但假如我又遇到别的数独呢?这就是我们需去考虑的问题。我们不是为了去解决一个数独,而是要解决所有的数独,当然,我们不仅仅只是为了解决所有数独,而是要用程序解决所以需要耗时耗力的问题。
要解决掉所有的数独,需要懂数独的规则,编写一个程序一定要要思路,有想法。首先数独,有它的唯一性,每一行,每一列,每一宫中都只能出现一次,那么我们就可以用循环从左上角来对这个数独进行填写,根据它的唯一性,假如不符合,我们就换一个数,再判断是否符合,假如都不符合则返回上一步,判断上一步的下一个数是否符合,如果还是都不符合,则返回上上步,直至所有的都符合数独的唯一性。(我们解数独的时候是进行推理,逻辑思考。电脑解数独的时候是穷举,尝试所有的可能性,这里用到的方法是回溯法,我们也可以用这种方法解,但人的速度太慢了,肯定没有电脑速度快,这就是电脑的优势)
效果图
源码
///
//
// 程序名称:数独解题器
// 编译环境:Mictosoft Visual Studio 2013, EasyX_20200315(beta)
//
#include <graphics.h>
#define WIDTH 480
#define HEIGHT 640
const wchar_t wPROGRAMINFO[] = _T("\n操作说明:\n1.鼠标左击下方不同的数字进行选取 \n2.再左击九宫格相应位置进行填入 \n3.选择后可重复填入 \n4.点击求解键后开始处理数独 \n4.点击清空将清除掉九宫格内的数\n");
int matrix[9][9] = { 0 }; // 定义一个二维数组储存数独
void drawframe(); // 绘制九宫格及修饰的相关图形
void grain(); // 纹路
void Prompt(); // 绘制提示符
void Solution(); // 求解按钮
void ClearButton(); // 清空按钮
void OUTTEXT(int i,int x,int y); // 绘制输出数字
int MouseMessage(int *m_x, int *m_y); // 处理鼠标消息,返回数与鼠标的坐标
void retrace(int number, COLORREF color); // 重新复原边框颜色等
void save(int n, int x,int y); // 储存和输出
void ClearScreen(); // 清空九宫格
bool FirstCheck(); // 初次判断检查
void Output(); // 输出答案
int MouseNumber = 0;
int sum = 0; // 数独多解的记录
int trace(int x, int y); // 算法的核心回溯法
int check(int x, int y); // 每次判断
bool newmatrix[9][9];
void Tofalse(); // 将数组全部置为 false
int CheckNumber(int n); // 判断里面有没有这个数
int Totrue(int n);
bool SecondCheck(); // 第二次判断
bool Point[3][3];
int main()
{
initgraph(WIDTH, HEIGHT);
drawframe(); // 绘制表格框架
while (true)
{
while (true)
{
int m_x, m_y, number;
number = MouseMessage(&m_x, &m_y); // 鼠标消息
if (number == 10)
{
MouseNumber = 0;
if (FirstCheck() == false)
continue;
else
break;
}
save(number, m_x, m_y);
if (FirstCheck() == false)
{
settextstyle(20, 0, _T("楷体"));
settextcolor(0xf4b1a4);
outtextxy(120, 99, _T("输入有误,请检查!"));
}
if (FirstCheck() == true)
{
settextstyle(20, 0, _T("楷体"));
outtextxy(120, 99, _T(" "));
}
}
settextcolor(0xf4b1a4);
settextstyle(20, 0, _T("楷体"));
outtextxy(120, 99, _T("求解中......"));
if (SecondCheck() == true) // 第二次检查,防止不必要的死循环
{
trace(0, 0); // 溯回法判断
}
else sum = 0;
if (sum == 0)
{
setbkmode(OPAQUE);
settextcolor(0xf4b1a4);
settextstyle(20, 0, _T("楷体"));
outtextxy(120, 99, _T(" "));
outtextxy(120, 99, _T("该数独无解!"));
continue;
}
if (sum == 1)
{
setbkmode(OPAQUE);
settextcolor(0xf4b1a4);
settextstyle(20, 0, _T("楷体"));
outtextxy(120, 99, _T(" "));
outtextxy(120, 99, _T("该数独只有一个解"));
sum = 0;
continue;
}
if (sum > 1)
{
setbkmode(OPAQUE);
settextcolor(0xf4b1a4);
settextstyle(20, 0, _T("楷体"));
outtextxy(120, 99, _T(" "));
outtextxy(120, 99, _T("该数独有多个解"));
sum = 0;
continue;
}
}
closegraph();
return 0;
}
// 绘制数独框架,可以进一步美化
void drawframe()
{
setbkcolor(0x3a0a0a);
cleardevice();
grain();
setlinestyle(PS_SOLID | PS_ENDCAP_SQUARE, 0);
setlinecolor(0x5555FF);
int x = 60;
int y = 120;
// 绘制九宫格,左上角坐标为(x ,y)每隔40画一条横线,并画一条竖线
for (int i = 0; i <= 9; i++)
{
line(x, y + i * 40, 420, y + i * 40);
line(x + i * 40, y, x + i * 40, 480);
}
setlinestyle(PS_SOLID | PS_ENDCAP_SQUARE, 3);
setlinecolor(0xFF5555);
// 分割为九份,左上角坐标为(x , y)每隔120画一条横线,并画一条竖线
for (int i = 0; i < 4; i++)
{
line(x, y + i * 120, 420, y + i * 120);
line(x + i * 120, y, x + i * 120, 480);
}
// 绘制汉字修饰符
settextcolor(0xf4b1a4);
setbkmode(TRANSPARENT);
settextstyle(76, 0, _T("楷体"));
outtextxy(60, 22, _T("数独辅助器"));
settextstyle(20, 0, _T("楷体"));
outtextxy(40, 490, _T("请选择你需要填入的数:"));
// 绘制下边的框格
line(40, 520, 440, 520);
line(40, 560, 440, 560);
for (int i = 0; i <= 10; i++)
{
line(40 + 40 * i, 520, 40 + 40 * i, 560);
}
// 给框格内填入数字
for (int i = 0; i <= 9; i++)
{
setbkmode(TRANSPARENT);
OUTTEXT(i, 40 + 40 * i + 12, 522);
}
settextcolor(0xf4b1a4);
// 绘制求解键
Solution();
// 绘制提示说明符
Prompt();
// 绘制清屏按钮
ClearButton();
settextstyle(20, 0, _T("楷体"));
outtextxy(60, 99, _T("提示:"));
}
// 绘制提示说明符
void Prompt()
{
setbkmode(TRANSPARENT);
setlinestyle(PS_SOLID | PS_ENDCAP_FLAT, 2);
settextstyle(42, 0, L"Webdings");
wchar_t c = 0x69;
outtextxy(438, 0, c);
}
// 求解按钮
void Solution()
{
setbkmode(TRANSPARENT);
rectangle(360, 580, 440, 620);
setbkmode(TRANSPARENT);
settextstyle(36, 0, _T("楷体"));
outtextxy(364, 582, _T("求解"));
}
// 清空按钮
void ClearButton()
{
setbkmode(TRANSPARENT);
setlinestyle(PS_SOLID | PS_ENDCAP_FLAT, 3);
rectangle(280, 580, 360, 620);
settextstyle(36, 0, _T("楷体"));
outtextxy(284,582,_T("清空"));
}
// 纹路
void grain()
{
setlinestyle(PS_SOLID | PS_ENDCAP_FLAT, 2);
setlinecolor(0xb82727);
line(61, 486, 0, 547);
line(94, 486, 0, 580);
line(140, 486, 7, 619);
line(158, 486, 15, 629);
line(170, 491, 24, 638);
line(170, 491, 212, 491);
line(212, 491, 280, 560);
line(280, 560, 400, 560);
line(400, 560, 480, 480);
line(228, 486, 271, 530);
line(271, 530, 352, 530);
line(352, 530, 396, 486);
line(40, 640, 105, 575);
line(105, 575, 114, 575);
line(114, 575, 168, 520);
line(168, 520, 220, 520);
line(220, 520, 300, 600);
line(300, 600, 360, 600);
setfillcolor(WHITE);
solidcircle(360, 600, 4);
line(60, 640, 110, 590);
line(110, 590, 119, 590);
line(119, 590, 177, 530);
line(177,530,211,530);
line(211, 530, 290, 608);
line(290, 608, 290, 632);
solidcircle(290, 632, 4);
circle(137, 593, 3);
line(139, 591, 179, 551);
line(179, 551, 213, 551);
line(213, 551, 242, 582);
line(242, 582, 242, 605);
solidcircle(242, 605, 4);
circle(159, 592, 3);
line(161, 589, 171, 579);
line(171, 579, 213, 579);
line(213, 579, 220, 585);
line(220, 585, 220, 592);
circle(220, 595, 3);
circle(110 ,600, 3);
line(113, 604, 128, 619);
line(128, 619, 260, 619);
line(260, 619, 280, 639);
line(280, 639, 339, 639);
line(339, 639, 379, 600);
line(379, 600, 379, 576);
circle(379, 573, 3);
circle(77, 637, 3);
line(80, 633, 95, 619);
line(95, 619, 117, 619);
line(117, 619, 125, 626);
line(125, 626, 210, 626);
line(210, 626, 222, 640);
line(88, 640, 93, 634);
line(93, 634, 199, 634);
line(199, 634, 202, 640);
line(358, 639, 397, 599);
line(397, 599, 438, 599);
line(438, 599, 470, 568);
circle(472, 565, 3);
line(379, 639, 398, 619);
line(398, 619, 420, 619);
circle(423, 619, 3);
circle(426, 568, 3);
line(429, 565, 480, 516);
line(458, 638, 467, 630);
line(467, 630, 480, 630);
line(0, 184, 26, 210);
line(26, 210, 26, 369);
line(26, 369, 0, 393);
line(0, 205, 7, 211);
line(7, 211, 7, 270);
circle(7, 273, 3);
line(0, 463, 5, 457);
line(5, 457, 5, 430);
line(5, 430, 51, 383);
line(51, 174, 40, 163);
line(40, 163, 40, 16);
circle(40, 13, 3);
line(52, 0, 69, 18);
line(69, 18, 69, 52);
circle(69, 55, 3);
line(257, 0, 144, 111);
line(144, 111, 144, 120);
line(310, 0, 190, 120);
line(238, 120, 263, 94);
line(263, 94, 329, 94);
line(329, 94, 424, 0);
line(423, 461, 423, 421);
line(423, 421, 445, 397);
circle(451, 392, 3);
line(420, 358, 480, 358);
line(420, 136, 480, 74);
line(420, 186, 443, 186);
line(443, 186, 480, 147);
line(420, 207, 450, 206);
line(450, 206, 480, 179);
line(420, 254, 480, 254);
line(420, 261, 480, 261);
// 粗白线
setlinestyle(PS_SOLID | PS_JOIN_BEVEL, 4);
line(70, 120, 70, 107);
line(70, 107, 138, 40);
line(138, 40, 138, 0);
line(133, 120, 133, 95);
line(212, 120, 331, 0);
line(420, 168, 429, 168);
line(429, 168, 480, 117);
line(420, 227, 462, 227);
line(462, 227, 480, 208);
line(0, 145, 45, 190);
line(45, 190, 45, 368);
line(45, 368, 0, 412);
line(133, 95, 227, 0);
line(344, 120, 460, 0);
line(119, 486, 0, 605);
setlinecolor(0xf4b1a4);
line(420, 268, 480, 268);
line(48, 471, 0, 520);
circle(464, 612, 3);
line(467, 609, 480, 597);
}
// 输出相应位置对应的相应的数
void OUTTEXT(int i, int x, int y)
{
settextstyle(36, 0, _T("consolas"));
switch (i)
{
case 0: settextcolor(0xFFFFFF); outtextxy(x, y, _T(" ")); break;
case 1: settextcolor(0xEFFFFE); outtextxy(x, y, _T("1")); break;
case 2: settextcolor(0xDFFFFD); outtextxy(x, y, _T("2")); break;
case 3: settextcolor(0xCFFFFC); outtextxy(x, y, _T("3")); break;
case 4: settextcolor(0xBFFFFB); outtextxy(x, y, _T("4")); break;
case 5: settextcolor(0xAFFFFA); outtextxy(x, y, _T("5")); break;
case 6: settextcolor(0x9FFFF9); outtextxy(x, y, _T("6")); break;
case 7: settextcolor(0x8FFFF8); outtextxy(x, y, _T("7")); break;
case 8: settextcolor(0x7FFFF7); outtextxy(x, y, _T("8")); break;
case 9: settextcolor(0x6FFFF6); outtextxy(x, y, _T("9")); break;
}
}
// 有鼠标获取相应位置相应的数
int MouseMessage(int *myx, int *myy)
{
MOUSEMSG m; // 定义鼠标消息
bool T = true;
while (T)
{
m = GetMouseMsg(); // 获取一个鼠标消息
setlinecolor(RED);
switch (m.uMsg)
{
case WM_LBUTTONDOWN:
if (m.x >= 40 && m.x <= 440 && m.y >= 520 && m.y <= 560) // 如果左键按下的范围在下方选择的范围内
{
if (MouseNumber >= 0) // 如果这个鼠标已经过数,继续点击,需要使得画过的颜色复原
{
retrace(MouseNumber, 0xFF5555);
}
retrace((m.x - 40) / 40, RED);
MouseNumber = (m.x - 40) / 40;
}
if (m.x >= 60 && m.x <= 420 && m.y >= 120 && m.y <= 480) // 坐标点在九宫格内就将给点坐标按地址值传出
{
*myx = m.x;
*myy = m.y;
T = false;
}
if (m.x >= 360 && m.x <= 440 && m.y >= 580 && m.y <= 620) // 确定键按按下
{
setlinecolor(RED);
settextcolor(RED);
Solution();
retrace(MouseNumber, 0xFF5555);
settextstyle(20, 0, _T("楷体"));
outtextxy(120, 99, _T(" "));
break;
}
if (m.x >= 436 && m.x <= 480 && m.y >= 0 && m.y <= 44) // 提示符按下
{
settextcolor(RED);
setlinecolor(RED);
Prompt();
break;
}
if (m.x >= 280 && m.x <= 360 && m.y >= 580 && m.y <= 620) // 清屏按下
{
setlinecolor(RED);
settextcolor(RED);
ClearButton();
retrace(MouseNumber, 0xFF5555);
setbkmode(OPAQUE);
settextstyle(20, 0, _T("楷体"));
outtextxy(120, 99, _T(" "));
break;
}
case WM_LBUTTONUP: // 左键抬起
if (m.x >= 360 && m.x <= 440 && m.y >= 580 && m.y <= 620) // 确定键抬起
{
setlinecolor(0xFF5555);
settextcolor(0xf4b1a4);
Solution();
MouseNumber = 10;
return MouseNumber;
}
if (m.x >= 436 && m.x <= 480 && m.y >= 0 && m.y <= 44) // 提示符抬起弹出操作说明
{
settextcolor(0xFF5555);
setlinecolor(0xFF5555);
Prompt();
MessageBox(NULL, wPROGRAMINFO, _T("关于"), MB_OK | MB_ICONASTERISK);
}
if (m.x >= 280 && m.x <= 360 && m.y >= 580 && m.y <= 620) // 清屏抬起
{
settextcolor(0xf4b1a4);
setlinecolor(0xFF5555);
ClearButton();
ClearScreen();
sum=0;
break;
}
}
}
return MouseNumber;
}
// 重新描矩形,给人按钮的感觉
void retrace(int number, COLORREF color)
{
setlinestyle(PS_SOLID | PS_ENDCAP_FLAT, 3);
setlinecolor(color);
switch (number)
{
case 0: rectangle(40 + 40 * 0, 520, 80 + 40 * 0, 560); break;
case 1: rectangle(40 + 40 * 1, 520, 80 + 40 * 1, 560); break;
case 2: rectangle(40 + 40 * 2, 520, 80 + 40 * 2, 560); break;
case 3: rectangle(40 + 40 * 3, 520, 80 + 40 * 3, 560); break;
case 4: rectangle(40 + 40 * 4, 520, 80 + 40 * 4, 560); break;
case 5: rectangle(40 + 40 * 5, 520, 80 + 40 * 5, 560); break;
case 6: rectangle(40 + 40 * 6, 520, 80 + 40 * 6, 560); break;
case 7: rectangle(40 + 40 * 7, 520, 80 + 40 * 7, 560); break;
case 8: rectangle(40 + 40 * 8, 520, 80 + 40 * 8, 560); break;
case 9: rectangle(40 + 40 * 9, 520, 80 + 40 * 9, 560); break;
}
}
// 对相应数的填入和储存
void save(int n, int x, int y)
{
int myx, myy;
myx = ((x - 60) / 40) * 40 + 60 + 12;
myy = ((y - 120) / 40) * 40 + 120 + 2;
setbkmode(OPAQUE);
OUTTEXT(n, myx, myy);
int mx, my;
mx = (x - 60) / 40;
my = (y - 120) / 40;
matrix[mx][my] = n;
}
// 将九宫格内的数清空
void ClearScreen()
{
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
setbkmode(OPAQUE);
OUTTEXT(0,i * 40 + 60 + 12, j * 40 + 120 + 2);
matrix[i][j] = 0;
}
}
MouseNumber = 0;
}
// 将答案输出
void Output()
{
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
setbkmode(OPAQUE);
OUTTEXT(matrix[i][j], 60 + i * 40 + 12, 120 + j * 40 + 2);
}
}
}
// 检查一行一列中的数,和九宫格中
int check(int x, int y)
{
int flag = 1;
for (int i = 0; i<9; i++)
{
if (matrix[x][i] == matrix[x][y] && i != y)
{
flag = 0;
}
if (matrix[i][y] == matrix[x][y] && i != x)
{
flag = 0;
}
}
int xi = x / 3, yi = y / 3;
for (int i = xi * 3; i<(xi + 1) * 3; i++)
{
for (int j = yi * 3; j<(yi + 1) * 3; j++)
{
if (i != x && j != y && matrix[i][j] == matrix[x][y])
{
flag = 0;
}
}
}
return flag;
}
// 核心算法,回溯法
int trace(int x, int y)
{
if (x == 9)
{
Output();
sum++;
}
if (sum > 1)
return 0;
if (matrix[x][y] == 0)
{
for (int j = 1; j <= 9; j++)
{
matrix[x][y] = j;
if (check(x, y))
{
trace(x + (y + 1) / 9, (y + 1) % 9);
}
matrix[x][y] = 0;
}
}
else
{
trace(x + (y + 1) / 9, (y + 1) % 9);
}
return 0;
}
// 初步进行判断数独的正确性
bool FirstCheck()
{
int a[9]; // 行
int b[9]; // 列
int c[9]; // 块
// 判断每一行是否符合条件
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
a[j] = matrix[i][j];
if (j == 8)
{
for (int q = 0; q < 9; q++)
{
for (int p = q + 1; p < 9; p++)
{
if (p < 9)
{
if (a[q] != 0 && a[p] != 0)
{
if (a[q] == a[p])
return false;
}
}
}
}
}
}
}
// 判断每一列是否符合条件
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
b[j] = matrix[j][i];
if (j == 8)
{
for (int q = 0; q < 9; q++)
{
for (int p = q + 1; p < 9; p++)
{
if (p < 9)
{
if (b[q] != 0 && b[p] != 0)
{
if (b[q] == b[p])
return false;
}
}
}
}
}
}
}
// 用于判断九宫格中每一个块中有无重复的
int ns = 0;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
for (int p = 0; p < 3; p++)
{
for (int q = 0; q < 3; q++)
{
c[ns] = matrix[i * 3 + p][j * 3 + q];
ns++;
if (ns == 9)
{
for (int w = 0; w < 9; w++)
{
for (int z = w + 1; z < 9; z++)
{
if (z < 9)
{
if (c[w] != 0 && c[z] != 0)
{
if (c[w] == c[z])
return false;
}
}
}
}
ns = 0;
}
}
}
}
}
// 如果依次判断后,无重复,则返回正确
return true;
}
// 将数组全部置为 false
void Tofalse()
{
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
newmatrix[i][j] = false;
}
}
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
Point[i][j] = false;
}
}
}
// 判断九宫格里面有没有这个数
int CheckNumber(int n)
{
int c[9];
int ns = 0;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
for (int p = 0; p < 3; p++)
{
for (int q = 0; q < 3; q++)
{
c[ns] = matrix[i * 3 + p][j * 3 + q];
ns++;
if (ns == 9) //将每一宫中的数存储到一个一维数组中进行判断
{
for (int w = 0; w < 9; w++)
{
if (c[w] == n)
Point[i][j] = true;
}
ns = 0;
}
}
}
}
}
return 0;
}
// 对行列进行赋值
int Totrue(int number)
{
for (int i = 0; i < 9; i++)
{
for (int j = 0; j < 9; j++)
{
if (matrix[i][j] == number)
{
for (int p = 0; p < 9; p++)
{
newmatrix[i][p] = true;
newmatrix[p][j] = true;
}
}
if (matrix[i][j] != 0)
{
newmatrix[i][j] = true;
}
}
}
return 0;
}
// 第二次判断
bool SecondCheck()
{
for (int number = 1; number < 10; number++)
{
Tofalse(); // 将数组初始化为假
CheckNumber(number); // 将有这个数的宫格进行赋值
Totrue(number); // 将含有这个数的行列都赋为真
// 开始判断第一个宫格是否存在这个数,如果存在,则判断下一个,如果存在,判断它里面是否为假
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
if (Point[i][j] == false)
{
int nums = 0;
for (int p = 0; p < 3; p++)
{
for (int q = 0; q < 3; q++)
{
if (newmatrix[i * 3 + p][j * 3 + q] == false)
{
nums++;
}
}
}
if (nums>0)
{
nums = 0;
}
else
return false;
}
}
}
}
return true;
}
以上就是C语言实现数独辅助器(附源码)的详细内容,更多关于C语言数独辅助器的资料请关注编程网其它相关文章!
免责声明:
① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。
② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341