数独九宫格

数独九宫格游戏

一.题目说明 

数独的游戏规则:

1、在9×9的大九宫格内,已给定若干数字,其他宫位留白,玩     家需要自己按照逻辑推敲出剩下的空格里是什么数字。
   2、必须满足的条件:每一行与每一列都有1到9的数字,每个小    九宫格里也有1到9的数字,并且一个数字在每行、每列及每   个小九宫格里只能出现一次,既不能重复也不能少。
   3、每个数独游戏都可根据给定的数字为线索,推算解答出来。

按照数独的游戏规则,用计算机实现已知数独的求解和数独题目的出题。

二.数据结构说明

数据结构一维数组、二维数组以及类似于“栈”的数据结构。

主要操作有:进栈,出栈,栈顶元素的操作等等

三.抽象数据类型

五个全局变量数组,其中两个二维数组,三个一维数组。

int a[10][10]

int sd[82]

int fix[82]

int
possible[82][10]

int stack[82]

四.算法设计

程序可以考虑人工智能的算法。采用人机的游戏方式,这里采用“回溯”的方法解决数独问题。

基本框架如下:

 

 图片粘贴不了

 

五.数独程序代码:

   #include"stdio.h" //标准输入输出头文件

#include"conio.h" //包含getch()的头文件

#include"stdlib.h" //包含rand()的头文件

#include"assert.h" //包含assert()的头文件

#include"time.h" //包含srand()的头文件

int a[10][10];//用来接收输入数据的数组

int sd[82];//处理题目以及保存最终结果

int fix[82];//记录哪些位置是确定的,确定为1,否则为0

int possible[82][10];//记录所有未确定数字的可能性

int stack[82];//用来放置填入的数的栈

int t;

void clssd()//初始化函数,所有位置设为0

{

   int
i,j,k;

   for(i=0;i<9;i++)

      for(j=0;j<9;j++)

         a[i][j]=0;

   for(k=1;k<=81;k++)

      sd[k]=0;

}

int line(int line,int value)//检验行

{

   int
i;

   for(i=1;i<=9;i++)

   {

      if(a[line][i]==value)
return 0;

   }

   return
1;

}

int row(int row,int value)//检验列

{

   int
i;

   for(i=1;i<=9;i++)

   {

      if(a[i][row]==value)
return 0;

   }

   return
1;

}

int square(int line,int row,int
value)//
检验3*3的九宫

{

   int
L,R,i,j;

   L=(line%3!=0)+line/3;//L表示所在九宫的行数

   R=(row%3!=0)+row/3;//R表示所在九宫的列数

   for(i=(L-1)*3+1;i<=L*3;i++)

   {

      for(j=(R-1)*3+1;j<=R*3;j++)

         if(a[i][j]==value)
return 0;

   }

   return
1;

}

   int
transform_to_line(int i)

   {

      int
line;

      line=i/9+(i%9!=0);

      return
line;

   }

   int
transform_to_row(int i)

   {

      int
row;

      row=i%9+9*(i%9==0);

      return
row;

   }

void transform_to_a(int i)

{

      int
l,r;

      l=transform_to_line(i);

      r=transform_to_row(i);

      a[l][r]=sd[i];

}

void transform_to_sd()

{

   int
line,row,i=1;

  

   for(line=1;line<=9;line++)

      for(row=1;row<=9;row++)

         do

         {

            sd[i]=a[line][row];

            i++;       

            break;

         }while(i<=81);

 

}

//输出函数

void printAll()

{

   int
i;

   for(i=0;i<81;i++)

   { 

      if(i%9==0)

         printf("\n\n\t\t");

      printf("%4d",sd[i+1]);

  

   }

   printf("\n\n\n");

}

void input()//输入数据到a[10][10]

{

   system("cls");//清屏

   int
b[3]={0,0,0},i,j;

   printf("请输入已知数据");

   printf("\n\n例如输入7 8 9,表示第8行 第9列的数值为7,以此类推。\n\n\n");

   do

   {       

      printf("\n输入数据:按照 值 / 行 / 列的顺序,以0结束");

      for(i=0;i<3;i++)

      {

         j=getch()-48;

         if(j==0&&i==0)
break;//
实现按0结束语句

         if(j>0&&j<10)

         {

            b[i]=j;

            printf("%3d",b[i]);

         }

         else
i--;

      }

      a[b[1]][b[2]]=b[0];  

   }while(j); 

   transform_to_sd();

   printf("\n\n\n\t您输入的题目为:\n\n");//打印输入的数独

   printAll();

}

//三个重要函数

bool beExist(int i,int j)//判断 sd 数组中位置为 i 的元素是否存在

{

   int
l,r;

   l=transform_to_line(i);

   r=transform_to_row(i);

   //if(sd[i]!=0)
return 1;
关键的错误所在!!! 

   if(line(l,j)*row(r,j)*square(l,r,j)==0)
return 1;

   else
return 0;

}

void setPb()//控制possible数组

{

  

   int
i,j;

   for(i=1;i<=81;i++)

   {    

      for(j=1;j<=9;j++)

      {

         if(sd[i]!=0)
possible[i][j]=-1;

         else
if( beExist(i,j))

         {possible[i][j]=-1;}

         else

         {possible[i][j]=j;}

      }

   }

 

}

bool fixAll(int sd[],int fix[],int
possible[82][10])//
控制fix数组

{

   int
i,j;

   int
k[82]; 

   for(i=1;i<=81;i++)

   {

      if(sd[i]!=0)

      {fix[i]=1;}

      else

      {fix[i]=0;}

   } 

   for(i=1;i<=81;i++)

   {

      if(sd[i]!=0)
continue;

      if(fix[i]==0)

      {

         for(j=1;j<=9;j++)

         if(possible[i][j]!=-1)

            k[i]++;

      }

      if(k[i]==1)//如果存在只有一种可能的位置则将fix[i]改为1

         fix[i]=1;

         sd[i]=possible[i][j];

   } 

   for(i=1;i<=81;i++)//判断是否存在只有一种可能的位置,若没有返回0

   {

      if(k[i]==1)
return 1;

      else
return 0;

   }

}

//判断是否完成

int isFull(int sd[])

{

   int
i;

   for(i=1;i<=81;i++)

      if(sd[i]==0)
return 0;  

   return
1;

}

void preDo()//预处理

{

   while(1)

   {

      setPb();

      if(!fixAll(sd,fix,possible))
//
即不存在只有一种可能性的位置

         break;

      if(isFull(sd))
//
若已经推出全部结果

         break;

   }

   if(isFull(sd))

      printAll();//打印结果

 

}

int calculate()//填数独 (关键算法!!!)

{

   preDo();//预处理,找出所有的位置的可能数值

   if(isFull(sd))
return 1;

   int
top=0;

   //将所有为0的位置入栈

   for(int
i=1;i<82;i++)

   { 

      if(fix[i]==0)

         stack[top++]=i;

   }

   int
max=top;//
记录最大数目加1

   top=0;//指向栈顶

   int
temp;

   bool
flag=true;//
该标志位说明了当前是正常入栈

   while(1)

   {

      assert(top>=0);

      if(flag)

      {

         int
j;

         for(j=1;j<=9;j++)

            if(possible[stack[top]][j]!=-1&&!beExist(stack[top],j))

            //若top所示的位置上,可以安插j这个数值,则

            {

                fix[stack[top]]=1;

                sd[stack[top]]=j;

                transform_to_a(stack[top]);

                ++top;

                if(top>=max)
return 1;

                break;

            }

         if(j>9)//该空所有可能值都不可以,则退一格

         {

        

            --top;

            if(top<0)
{printAll(); return 0;}

            flag=false;

         }

      }    

      else

      {

         if(sd[stack[top]]==9)

         {

            fix[stack[top]]=0;

            sd[stack[top]]=0;

            transform_to_a(stack[top]);

            --top;

            if(top<0)
{printAll(); return 0;}

         }

         else

         {

            temp=sd[stack[top]];    

            temp++;

            while(possible[stack[top]][temp]==-1||beExist(stack[top],temp))

            {

                temp++;

                if(temp>9)

                break;

            }

            if(temp>9)//当前节点没有更换的可能性,继续退回

            {

                fix[stack[top]]=0;

                sd[stack[top]]=0;

                transform_to_a(stack[top]);

                --top;

                if(top<0)
{printAll(); return 0;}

            }

            else

            {

                sd[stack[top]]=temp;

                transform_to_a(stack[top]);

                ++top;

                flag=true;

            }

         }

     

      }

   }

  

}

void solve_problem()//解题

{

   int
p;

   system("cls");
//
清屏

   clssd();
//
赋初值为0

   input();
//
输入数据

   transform_to_sd();
//
转换为sd[i]数组

   p=calculate();
//
计算数独

   if(p==0)

      printf("\t题目有误!!! ");

      printf("\n\n\t答案为:\n");

   printAll();

void random()//从1-9中随机产生几个值输入sd[1]至sd[9]

   { 

      int
i,j,r;

      int
change=1;

      int
b[10]={0,0,0,0,0,0,0,0,0,0};

       for(i=1;i<=9;)//从1-9中随机产生几个值

       { 

         change=1;

         j=1+rand()%9;

         for(r=1;r<=9;r++)

            if(b[r]==j) change=0;

         if(change==1)

         {b[i]=j;  i++;}

       }

       

       for(i=1;i<=9;i++)

       {

         sd[i]=b[i];

         transform_to_a(i);

       }

   }

void hide()

   {

      int
i,f;

      printf("请输入难度:\n\n1.Easy\n\n2.Normal\n\n3.Hard\n\n4.So
Hard\n\n5.Terrible Hard\n\n");

      do

      {

         f=getch()-48;

      }while(f>5||f<1);//一共5个级别

  

      for(i=1;i<=81;i++)

      { 

         if(rand()%6>=f)//利用随机数出现的概率出题

         printf("%4d",sd[i]);

         else

            printf("   0");

         if(i%9==0)

            printf("\n\n");

      }

   }

void make_problem()//出题函数

   system("cls");//初始化

   clssd();

   random();//填9个随机值

   calculate();//算出答案

   hide();

   printf("\t\t\t注意:题目中0代表待填数据\n\n\t\t   按空格键输出答案,其他键退出程序\n");

   int
f;

   do

   {

      f=getch()-32;

      if(!f)

         printAll();

      else
break;

   }while(f);

}

void quit()

{

int i;

for(i=0;i<100;i++)

{

 printf("%d\n",i);

 if (i>2||i<1)

 {

 
exit(1);

 }

}

}

void main()//主函数

{

   srand((unsigned)time(0));//设置时间种子为0

   system("cls");//清屏

   clssd();

   printf("\n\t数独游戏\n\n\t1.你出题,电脑来解\n\n\t2.电脑出题,你来解\n\n\t3.退出游戏");

   int
i;

   do

   {

      i=getch()-48;

      switch(i)

      {

      case
1:solve_problem();

         break;

      case
2:make_problem();

         break;

      case
3:quit();

         break;

      }

   }while(i>2||i<1);

}

六.调试报告

1.整个程序最麻烦的地方是二维数组a[10][10]与一 维数组sd[82]之间的转换。因为输入数据为了方便 和符合人类的思维采用了与数独相似的二维数组   进行输入。但是回溯算法要求转换成一维数组进行  操作。

2.调试过程中,用到了一个宏命令assert(top>=0)  top是栈顶元素的“指针”,用来操控栈中元素。正  常情况下top>=0的。如果出现异常情况,则assert 函数会弹出对话框报错。这表示calculate函数在 回溯的时候top值总是会回到栈底元素之前。事实  上,开始因为在beExist()函数中用了错误的判断 方 式,运行程序时总是会使得top<0。

3.上一条中的错误之所以会出现,是因为在设计程序 之初没有想好,不够完善。于是出现了,自己设计 的判断方法与计算数独矛盾的结果。得到的教训   是,动手之前应该先考虑清楚完整的架构和应该考 虑到的细节.

 

七.测试结果分析

下面为规定测试数据:

图片粘贴不了

 

 

时间: 2024-10-11 00:19:08

数独九宫格的相关文章

CSDN日报20170414 ——《从菜鸟到架构师》

[程序人生]从菜鸟到架构师(专栏) 作者:李熠 本专栏是以博主的亲身经历讲述博主从菜鸟到架构师的修炼之路. [架构]分布式系统常见的事务处理机制 作者:kkkloveyou 为保障系统的可用性.可靠性以及性能,在分布式系统中,往往会设置数据冗余,即对数据进行复制.举例来说,当一个数据库的副本被破环以后,那么系统只需要转换到其他数据副本就能继续运行下去.另外一个例子,当访问单一服务器管理的数据的进程数不断增加时,系统就需要对服务器的数量进行扩充,此时,对服务器进行复制,随后让它们分担工作负荷,就可

Java for LeetCode 036 Valid Sudoku

Determine if a Sudoku is valid, according to: Sudoku Puzzles - The Rules. The Sudoku board could be partially filled, where empty cells are filled with the character '.'. 解题思路: 传说中的数独(九宫格)问题,老实遍历三个规则即可: JAVA实现: static public boolean isValidSudoku(cha

OpenCV玩九宫格数独(一)——九宫格图片中提取数字

前言 首先要明确我们的任务.要想解数独,需要进行计算,图片格式的数字肯定是不行的,所以必须把图片上的数字转换为实实在在的数字才能进行计算.要得到实实在在的数字,我们需要做的是对图片上的数字进行提取和识别.本文先说第一步,图片中数字的提取. 在一年之前,我曾用C++尝试过opencv解数独,但由于当时水平有限,未能完成.当时的成果就是透视变换的应用和方格数字的提取.现在稍微简化一下工作,不再从倾斜的数独图片中提取数独,而是直接用正拍且已经提取好的数独开始处理.这里用到的数独图片如下图所示: 方法

OpenCV玩九宫格数独(零)——预告篇

九宫格 数独源于18世纪的瑞士,又称九宫格,有九行.久列和九宫.玩家需要在九宫格中,根据已知的数字,利用逻辑和推理能力,填出所有的空格中应有的数字.填的时候要求每行.每列和每宫都要不重复地包含数字0-9.每行.每列和每宫中1-9都必须出现且只能出现一次,故称之为数独.数独游戏考察的是解题者的观察能力和逻辑推理能力,虽然规则很简单,但是数字的排列方式却是包含千变万化,是一种锻炼思维的绝佳方式.有时候数独不光有数字的变化,还有颜色的变化,更难但趣味也更多. 在刚刚接触机器视觉的时候,我就想着用机器视

九宫格数独--回溯法

数独顾名思义——每个数字只能出现一次.数独是一种源自18世纪末的瑞士,后在美国发展.并在日本得以发扬光大的数字谜题.数独盘面是个九宫,每一宫又分为九个小格.在这八十一格中给出一定的已知数字和解题条件,利用逻辑和推理,在其他的空格上填入1-9的数字.使1-9每个数字在每一行.每一列和每一宫中都只出现一次. 这种游戏全面考验做题者观察能力和推理能力,虽然玩法简单,但数字排列方式却千变万化,所以不少教育者认为数独是训练头脑的绝佳方式. 九宫格的游戏在很多地方都经常出现,比如手机,电脑等等,下面就以简单

九宫格练习 9*9数独游戏

#include <algorithm> #include <bitset> #include <cassert> #include <cctype> #include <cfloat> #include <climits> #include <cmath> #include <complex> #include <cstdio> #include <cstdlib> #include

微信小程序:数独挑战之九宫格-中级-第一题解题思路

题目 中级第一题相对比较简单,只要运行唯一余数.排除法基本就能解出来,下面看一下解题路径: 最终答案为: 原文地址:https://www.cnblogs.com/webjlwang/p/10612671.html

第二次作业——个人项目实战:数独

1.阅读 刚开始泛看<构建之法>的时候,还是觉得非常难理解里面的内容,特别是代码部分.后来第二次拿起这本书,从第一章开始看起,慢慢了解了"软件企业=软件+商业模式"和对软件工程的定义,但是更多地还是记忆了一遍,没有明白这里面的深意:看第二章的时候,跟着单元测试.回归测试的.效能分析的内容自己照着书上的代码敲了一敲,偶尔会出现bug,但是能得到书上所说的效果还是很开心的,效能分析,感觉就是代码的效率问题,追求高效,然后接触到了软件工程师的一套模型-个人开发流程PSP,我也尝试

洛谷 【P1074】靶形数独

P1074 靶形数独 题目描述 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他 们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教, Z 博士拿出了他最近发明的“靶形数独”,作为这两个孩子比试的题目. 靶形数独的方格同普通数独一样,在 9 格宽×9 格高的大九宫格中有 9 个 3 格宽×3 格 高的小九宫格(用粗黑色线隔开的).在这个大九宫格中,有一些数字是已知的,根据这些数字,利用逻辑推理,在其他的空格上填入 1 到 9 的数字.每