#include<iostream> #include<cstdlib> #include<ctime> #include<fstream> using namespace std; ofstream ocout; int sudo[9][9]; bool set(int x,int y,int val) { if(sudo[x][y]!=0) //非空 return false; int x0,y0; for(y0=0;y0<9;y0++) //行衝突 { if(sudo[x][y0]==val) return false; } for(x0=0;x0<9;x0++) //列衝突 { if(sudo[x0][y]==val) return false; } for(x0=x/3*3;x0<x/3*3+3;x0++) //格衝突 { for(y0=y/3*3;y0<y/3*3+3;y0++) { if(sudo[x0][y0]==val) return false; } } sudo[x][y]=val; return true; } void current(int* cur) ///0~9隨機序列 { int i,j,temp; for(int i=0;i<9;i++) { cur[i]=i; } for(int i=0;i<9;i++) { j=rand()%9; temp=cur[j]; cur[j]=cur[i]; cur[i]=temp; } } void reset(int x,int y) { sudo[x][y]=0; } bool fill(int x,int val) { int cur[9]; current(cur); //生成當前行的掃描序列 for(int i=0;i<9;i++) { int y=cur[i]; if(set(x,y,val)) //能夠先把每一行的1填了,再填每一行的2...以此類推 { if(x==8) //到了最後一行 { if(val==9||fill(0,val+1)) //當前填9則結束,不然從第一行填下一個數 return true; } else { if(fill(x+1,val)) //下一行繼續填當前數 return true; } reset(x,y); //回溯 } } return false; } void digHole(int holeCnt) //挖空 { int x,y; for(int i=0;i<2;i++) { x = rand() % 3; y = rand() % 3; while( sudo[x][y] == 0 ) { x = rand() % 3; y = rand() % 3; } sudo[x][y]=0; sudo[x][y+3]=0; sudo[x][y+6]=0; sudo[x+3][y]=0; sudo[x+3][y+3]=0; sudo[x+3][y+6]=0; sudo[x+6][y]=0; sudo[x+6][y+3]=0; sudo[x+6][y+6]=0; } for (int j = 0; j < holeCnt; j++ ) { y = rand() % 9; y = rand() % 9; while(sudo[x][y] == 0 ) { x= rand() % 9; y = rand() % 9; } sudo[x][y] = 0; } } void clear(int temp[9][9]) //清空 { for(int i=0;i<9;i++) { for(int j=0;j<9;j++) { temp[i][j]=0; } } } void printsudo() //輸出到屏幕 { for(int x=0;x<9;x++) { (x%3==0)?(cout<<" -----------------------\n "):(cout<<" "); cout<<"| "; for(int y=0;y<9;y++) { cout<<sudo[x][y]<<" "; (y%3==2)?(cout<<"| "):(cout<<""); } cout<<endl; } cout<<" -----------------------\n"<<endl; } void printsudotxt() //輸出到sudotiku.txt { for(int x=0;x<9;x++) { (x%3==0)?(ocout<<" -----------------------\n "):(ocout<<" "); ocout<<"| "; for(int y=0;y<9;y++) { ocout<<sudo[x][y]<<" "; (y%3==2)?(ocout<<"| "):(ocout<<""); } ocout<<endl; } ocout<<" -----------------------\n"<<endl; } int main() { srand((unsigned)time(NULL)); //這個是種子函數 爲rand函數提供不一樣的種子 每次運行程序產生不一樣的隨機數,否則rand函數每次運行程序產生的隨機數都是同樣的 ocout.open("sudotiku.txt"); cout<<"請輸入數獨棋盤題目個數N (0 < N <= 1000000):"<<endl; int N; cin>>N; cout<<"隨機生成"<<N<<"個不重複的已解答完畢的數獨棋盤以下:"<<endl; ocout << "隨機生成"<<N<<"個不重複的已解答完畢的數獨棋盤以下:" << endl; for(int i=0;i<N;i++) { clear(sudo); while(!fill(0,1)); digHole(rand()%31+30); printsudo(); printsudotxt(); } ocout.close(); return 0; }
輸入:數獨棋盤題目個數N (0 < N <= 1000000)ios
輸出:隨機生成N個不重複的有惟一解的數獨棋盤。挖空處用數字0表示,每一個數獨棋盤中至少要有30個以上的0。每一個棋盤隔一行,輸出須要到文件sudotiku.txt中。git
基於第二次數獨遊戲,添加GUI界面,實現如下功能: 函數
關於GUI界面的代碼,已上傳到Coding,網址:https://coding.net/u/dhlg_201810812001/p/sudo/git
生成數獨的GUI界面以下所示:
點擊自動計算數獨結果,就會生成數獨的惟一解,結果以下:
經測試,程序能夠正確運行,但如上游戲信息所提示,計算結果最多隻能保存1000個,有必定的侷限性,還需繼續完善。
此次做業相比上次而言,有必定的難度。關於GUI界面,因爲使用不是很熟練,因此只能參考別人的代碼來進行實現,我搜集了CSDN中關於數獨的一些代碼,對於經過MFC來進行繪圖這塊因爲以前沒有接觸過,因此只能借鑑別人的,遇到不懂的語句再去百度,經過不斷學習,對其結構有了必定的理解,達到了可以看懂的地步,但要想本身作出一個界面仍是有必定困難。
整個做業的完成花費了大量時間,大概有一個星期左右,主要是由於對於MFC的使用不熟練,經過本次做業的完成,我又掌握了一項新的技能——MFC的使用,爲之後深刻的學習奠基了一些基礎。