数独九宫格
數獨九宮格游戲
一.題目說明?
數獨的游戲規則:
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.上一條中的錯誤之所以會出現,是因為在設計程序 之初沒有想好,不夠完善。于是出現了,自己設計 的判斷方法與計算數獨矛盾的結果。得到的教訓?? 是,動手之前應該先考慮清楚完整的架構和應該考 慮到的細節.
?
七.測試結果分析
下面為規定測試數據:
?圖片粘貼不了
?
?
?
?
?
?
?
?
?
?
?
?
?
轉載于:https://www.cnblogs.com/wanghongcai/p/4839090.html
總結
- 上一篇: vmware 官方下载
- 下一篇: 2013年全国统一题库考试-小型汽车驾照