数独九宫格
數(shù)獨(dú)九宮格游戲
一.題目說(shuō)明?
數(shù)獨(dú)的游戲規(guī)則:
1、在9×9的大九宮格內(nèi),已給定若干數(shù)字,其他宮位留白,玩???? 家需要自己按照邏輯推敲出剩下的空格里是什么數(shù)字。
?? 2、必須滿(mǎn)足的條件:每一行與每一列都有1到9的數(shù)字,每個(gè)小??? 九宮格里也有1到9的數(shù)字,并且一個(gè)數(shù)字在每行、每列及每?? 個(gè)小九宮格里只能出現(xiàn)一次,既不能重復(fù)也不能少。
?? 3、每個(gè)數(shù)獨(dú)游戲都可根據(jù)給定的數(shù)字為線索,推算解答出來(lái)。
按照數(shù)獨(dú)的游戲規(guī)則,用計(jì)算機(jī)實(shí)現(xiàn)已知數(shù)獨(dú)的求解和數(shù)獨(dú)題目的出題。
二.數(shù)據(jù)結(jié)構(gòu)說(shuō)明
數(shù)據(jù)結(jié)構(gòu)一維數(shù)組、二維數(shù)組以及類(lèi)似于“棧”的數(shù)據(jù)結(jié)構(gòu)。
主要操作有:進(jìn)棧,出棧,棧頂元素的操作等等
三.抽象數(shù)據(jù)類(lèi)型
五個(gè)全局變量數(shù)組,其中兩個(gè)二維數(shù)組,三個(gè)一維數(shù)組。
int a[10][10]
int sd[82]
int fix[82]
int possible[82][10]
int stack[82]
四.算法設(shè)計(jì)
程序可以考慮人工智能的算法。采用人機(jī)的游戲方式,這里采用“回溯”的方法解決數(shù)獨(dú)問(wèn)題。
基本框架如下:
?
?圖片粘貼不了
?
?
五.數(shù)獨(dú)程序代碼:
?? #include"stdio.h" //標(biāo)準(zhǔn)輸入輸出頭文件
#include"conio.h" //包含getch()的頭文件
#include"stdlib.h" //包含rand()的頭文件
#include"assert.h" //包含assert()的頭文件
#include"time.h" //包含srand()的頭文件
int a[10][10];//用來(lái)接收輸入數(shù)據(jù)的數(shù)組
int sd[82];//處理題目以及保存最終結(jié)果
int fix[82];//記錄哪些位置是確定的,確定為1,否則為0
int possible[82][10];//記錄所有未確定數(shù)字的可能性
int stack[82];//用來(lái)放置填入的數(shù)的棧
int t;
void clssd()//初始化函數(shù),所有位置設(shè)為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)//檢驗(yàn)行
{
?? int i;
?? for(i=1;i<=9;i++)
?? {
????? if(a[line][i]==value) return 0;
?? }
?? return 1;
}
int row(int row,int value)//檢驗(yàn)列
{
?? 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)//檢驗(yàn)3*3的九宮
{
?? int L,R,i,j;
?? L=(line%3!=0)+line/3;//L表示所在九宮的行數(shù)
?? R=(row%3!=0)+row/3;//R表示所在九宮的列數(shù)
?? 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);
?
}
//輸出函數(shù)
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()//輸入數(shù)據(jù)到a[10][10]
{
?? system("cls");//清屏
?? int b[3]={0,0,0},i,j;
?? printf("請(qǐng)輸入已知數(shù)據(jù)");
?? printf("\n\n例如輸入7 8 9,表示第8行 第9列的數(shù)值為7,以此類(lèi)推。\n\n\n");
?? do
?? {???????
????? printf("\n輸入數(shù)據(jù):按照 值 / 行 / 列的順序,以0結(jié)束");
????? for(i=0;i<3;i++)
????? {
???????? j=getch()-48;
???????? if(j==0&&i==0) break;//實(shí)現(xiàn)按0結(jié)束語(yǔ)句
???????? 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");//打印輸入的數(shù)獨(dú)
?? printAll();
}
//三個(gè)重要函數(shù)
bool beExist(int i,int j)//判斷 sd 數(shù)組中位置為 i 的元素是否存在
{
?? int l,r;
?? l=transform_to_line(i);
?? r=transform_to_row(i);
?? //if(sd[i]!=0) return 1; 關(guān)鍵的錯(cuò)誤所在!!!?
?? if(line(l,j)*row(r,j)*square(l,r,j)==0) return 1;
?? else return 0;
}
void setPb()//控制possible數(shù)組
{
??
?? 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數(shù)組
{
?? 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++)//判斷是否存在只有一種可能的位置,若沒(mé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()//預(yù)處理
{
?? while(1)
?? {
????? setPb();
????? if(!fixAll(sd,fix,possible)) //即不存在只有一種可能性的位置
???????? break;
????? if(isFull(sd)) //若已經(jīng)推出全部結(jié)果
???????? break;
?? }
?? if(isFull(sd))
????? printAll();//打印結(jié)果
?
}
int calculate()//填數(shù)獨(dú) (關(guān)鍵算法!!!)
{
?? preDo();//預(yù)處理,找出所有的位置的可能數(shù)值
?? 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;//記錄最大數(shù)目加1
?? top=0;//指向棧頂
?? int temp;
?? bool flag=true;//該標(biāo)志位說(shuō)明了當(dāng)前是正常入棧
?? 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這個(gè)數(shù)值,則
??????????? {
??????????????? 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)//當(dāng)前節(jié)點(diǎn)沒(méi)有更換的可能性,繼續(xù)退回
??????????? {
??????????????? 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(); //輸入數(shù)據(jù)
?? transform_to_sd(); //轉(zhuǎn)換為sd[i]數(shù)組
?? p=calculate(); //計(jì)算數(shù)獨(dú)
?? if(p==0)
????? printf("\t題目有誤!!! ");
????? printf("\n\n\t答案為:\n");
?? printAll();
}?
void random()//從1-9中隨機(jī)產(chǎn)生幾個(gè)值輸入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中隨機(jī)產(chǎn)生幾個(gè)值
????? ?{?
???????? 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("請(qǐng)輸入難度:\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個(gè)級(jí)別
??
????? for(i=1;i<=81;i++)
????? {?
???????? if(rand()%6>=f)//利用隨機(jī)數(shù)出現(xiàn)的概率出題
???????? printf("%4d",sd[i]);
???????? else
??????????? printf("?? 0");
???????? if(i%9==0)
??????????? printf("\n\n");
????? }
?? }
void make_problem()//出題函數(shù)
{?
?? system("cls");//初始化
?? clssd();
?? random();//填9個(gè)隨機(jī)值
?? calculate();//算出答案
?? hide();
?? printf("\t\t\t注意:題目中0代表待填數(shù)據(jù)\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()//主函數(shù)
{
?? srand((unsigned)time(0));//設(shè)置時(shí)間種子為0
?? system("cls");//清屏
?? clssd();
?? printf("\n\t數(shù)獨(dú)游戲\n\n\t1.你出題,電腦來(lái)解\n\n\t2.電腦出題,你來(lái)解\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);
}
六.調(diào)試報(bào)告
1.整個(gè)程序最麻煩的地方是二維數(shù)組a[10][10]與一 維數(shù)組sd[82]之間的轉(zhuǎn)換。因?yàn)檩斎霐?shù)據(jù)為了方便 和符合人類(lèi)的思維采用了與數(shù)獨(dú)相似的二維數(shù)組?? 進(jìn)行輸入。但是回溯算法要求轉(zhuǎn)換成一維數(shù)組進(jìn)行? 操作。
2.調(diào)試過(guò)程中,用到了一個(gè)宏命令assert(top>=0)? top是棧頂元素的“指針”,用來(lái)操控棧中元素。正? 常情況下top>=0的。如果出現(xiàn)異常情況,則assert 函數(shù)會(huì)彈出對(duì)話(huà)框報(bào)錯(cuò)。這表示calculate函數(shù)在 回溯的時(shí)候top值總是會(huì)回到棧底元素之前。事實(shí)? 上,開(kāi)始因?yàn)樵赽eExist()函數(shù)中用了錯(cuò)誤的判斷 方 式,運(yùn)行程序時(shí)總是會(huì)使得top<0。
3.上一條中的錯(cuò)誤之所以會(huì)出現(xiàn),是因?yàn)樵谠O(shè)計(jì)程序 之初沒(méi)有想好,不夠完善。于是出現(xiàn)了,自己設(shè)計(jì) 的判斷方法與計(jì)算數(shù)獨(dú)矛盾的結(jié)果。得到的教訓(xùn)?? 是,動(dòng)手之前應(yīng)該先考慮清楚完整的架構(gòu)和應(yīng)該考 慮到的細(xì)節(jié).
?
七.測(cè)試結(jié)果分析
下面為規(guī)定測(cè)試數(shù)據(jù):
?圖片粘貼不了
?
?
?
?
?
?
?
?
?
?
?
?
?
轉(zhuǎn)載于:https://www.cnblogs.com/wanghongcai/p/4839090.html
總結(jié)
- 上一篇: vmware 官方下载
- 下一篇: 2013年全国统一题库考试-小型汽车驾照