1.数独题目生成程序(搜索算法)

前几天在玩数独游戏的时候,产生了一个大胆的想法:

数独app上的题目都是现成的,干脆自己写一个可以随机生成数独的程序算了

一、需求提出:

1.随机生成数独题目,要求该题目有解;

2.当填数违反数独操作(填到题目原本的数字去了,填数违规等)时,禁止操作,弹出提示;

3.当81格都填满时,判断对错;

二、需求分析

总:

需求2,3相对简单,尤其是需求3,都禁止违规操作了,填满81格不就赢了吗?也许是当初没有给自己再次组织语言的机会,提出了这么傻缺的需求,核心问题就是需求1。

需求1

首先得到一个9*9,符合数独规则的矩阵,然后随机挖空不就行了吗?所以需求1的问题转化为如何得到一个随机9*9数独矩阵。

第一个想出来的方法是用搜索算法,在每一个格子随机生成1-9的数字,然后判断符不符合规则,再下一个。

这个算法的缺点是效率感人,81个格子,每个格子的数字都要判断,每个格子的数字还都是随机生成的,效率能不感人吗?

优点是还没付诸行动就被否定了,给程序编写省下了不少时间

第二个想法就相对可靠了,先在随机在若干个格子上生成随机数(这些随机数也要符合数独规则),这样就变成了一道题目,然后让电脑用搜索算法求解,只要能解出一种,就停止解题,并且选取这种解答作为随机9*9数独矩阵以及本题的答案。解不出也没有关系重新生成随机数再重新解过就得了。

这个方法可行度很高,优点很多,其中最重要的优点是我在刷蓝桥杯时曾经写过一个解数独的程序

经过调试,我把随机数个数设置在了15,既不会因为太少而使得随机数在矩阵上分布不均匀,又保证不会因为数字太多,答案太少而影响生成效率。

得到这个矩阵后,经过简单的随机挖空,一道数独题目就随机生成了。

ps:我把这些随机生成的题目塞回解数独器求解,发现解法并不唯一,若要解法唯一则效率低下。在调试后把挖空个数控制在了46~51个。使得挖空不会太少,影响游戏性;挖空不会太多,让题目有成千上万种解

经过n(n>=30)次测试,在挖空46个的情况下,求解法个数m

1<m<10 和 9<m<100 的次数几乎各占一半,两者加起来约莫90%,m=1与m>99的情况则瓜分了剩下的10%

虽然达不到只有一个解的严格标准,但是,嗯,解比较少,也还凑合吧

需求2

我采取三位数输入的方法

百位是横标,十位是竖标,个位是填数,除个位外不得含9,个位不得为0,判断方法直接用解数独器现成的就行了

输入999表示弃权

程序运行结果:

开始

操作

弃权:

四.代码:

#include<stdio.h>
#include<stdlib.h>
#include<windows.h>
#include<ctime> 

#define big 7 //大方格边界颜色 
#define mid 2 //中方格边界颜色
#define small 8 //小方格边界颜色 
#define col 2 //题目原数据颜色 

int flag=1,total=0;		
		
void gotoxy(int x, int y){
	++x,++y; 
    COORD pos;//表示一个字符在控制台屏幕上的坐标
    pos.X = x;
    pos.Y = y;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);//是API中定位光标位置的函数。
}//光标控制模块

void color(int b){
    HANDLE hConsole = GetStdHandle((STD_OUTPUT_HANDLE)) ;
    SetConsoleTextAttribute(hConsole, b) ;//颜色控制
}//颜色控制模块

void Boundary(){
	gotoxy(3,1);
	color(big);
	printf("+---+---+---+---+---+---+---+---+---+");
	color(small);
	for(int i=1;i<10;++i){
		color(small);
		gotoxy(3,i*2+1);
		if(i==3 || i==6){
			color(mid);
		}
		printf("+---+---+---+---+---+---+---+---+---+");
		for(int j=0;j<10;++j){
			gotoxy(3+j*4,i*2);
			if(j==3 || j==6){
				color(mid);
			}
			if(j==0 || j==9){
				color(big);
			}
			printf("|   ");
			color(7);
			gotoxy(3+j*4,i*2+1);
			if(i%3!=0 || j%3!=0){
				color(small);
			}
			if(j==0 || j==9){
				color(big);
			}
			if((i%3==0 || j%3==0) && j!=0 && j!=9){
				color(mid);
			}
			printf("+");
			color(small);
		}
	}
	color(big);
	gotoxy(3,19);
	printf("+---+---+---+---+---+---+---+---+---+");
	color(7);
	gotoxy(60,1);
	printf("数独游戏");
	gotoxy(45,3);
	printf("哈蒙森软件工业公司1875年出品");
	gotoxy(45,5);
	printf("解法:0种"); 
	gotoxy(45,7);
	printf("操作中输入'678',表示对6行8列标记为8;输入'999'表示放弃游戏"); 
	for(int i=0;i<9;++i){
		gotoxy(4*i+5,0);
		printf("%d",i);
		gotoxy(4*i+5,20);
		printf("%d",i); 
		gotoxy(1,2*i+2);
		printf("%d",i);
		gotoxy(41,2*i+2);
		printf("%d",i);
	}
}

bool Flag(int m,int n,int b,int board[9][9]){
	int a[9];
	for(int i=0;i<9;++i){
		if(board[i][m]==b){
			return false;
		}
		if(board[n][i]==b){
			return false;
		}
	}
	for(int i=0;i<9;++i){
		a[i]=0;
	}
	a[0]=b;
	gotoxy(50,15);
	int q=(n/3)*3,p=(m/3)*3,r=1;
	for(int i=p;i<p+3;++i){
		for(int j=q;j<q+3;++j){
			if(board[j][i]>0 && board[j][i]<10){
				a[r]=board[j][i];++r;
			}
		}
	} 
	for(int i=0;i<9;++i){
		for(int j=i+1;j<9;++j){
			if(a[i]==a[j] && i!=j && a[i]!=0){
				return false;
			}
		}
	}
	return true;
}

void Sudoku(int board[9][9]){
	time_t seed;
	srand(time(&seed));
	int number=15,p=0,q=0,i=0;
	while(i<number){
		int m=rand()%9,n=rand()%9,b=rand()%9+1;
		if(Flag(n,m,b,board)==true){
			board[m][n]=b;
			p=m,q=n;
			++i; 
		}
	}
	board[p][q]=0;
} 

int bfs(int x,int y,int num,int board[9][9]){
	int m,n,time=0;
	int dir[9][2]={{1,1},{1,0},{1,-1},{0,1},{0,-1},{-1,1},{-1,0},{-1,-1},{0,0}};
	for(int i=0;i<9;++i){
		m=x+dir[i][0],n=y+dir[i][1];
		if(num==board[n][m]){
			++time;
		}
		if(time>1){
			return 0;
		}
	}
	return 1;
}

int Judge(int x,int y,int n,int board[9][9]){
	int t=0;
	for(int i=0;i<9;++i){
		if(board[y][i]==n){
			++t;
			if(t>1){
				return 1;
			}
		}
	}
	t=0;
	for(int i=0;i<9;++i){
		if(board[i][x]==n){
			++t;
			if(t>1){
				return 1;
			}
		}
	}
	int nu,m;
	if(bfs(1+(x/3)*3,1+(y/3)*3,n,board)==0){
		return 1;
	}
}

void Show(int input,int board[9][9],int sudoku[9][9][2]){//30<=input<=55 
	time_t seed;
	srand(time(&seed));
	int i=0;
	color(col);
	for(int i=0;i<9;++i){
		for(int j=0;j<9;++j){
			gotoxy(5+j*4,i*2+2);
			printf(" ");
			board[i][j]=0; 
		}
	}
	while(i<input){
		int m=rand()%9,n=rand()%9;
		if(sudoku[m][n][1]==0){
			gotoxy(5+n*4,m*2+2);
			printf("%d",sudoku[m][n][0]);
			sudoku[m][n][1]=1;
			board[m][n]=sudoku[m][n][0];
			++i;
		}
	}
} 

void Sel(int x,int y,int kind,int board[9][9],int sudoku[9][9][2],int &total){
	if(flag==0 && kind==0){
		return;
	}
	if(y<9){
		if(x<9){
			if(board[y][x]<1 || board[y][x]>9){
				for(int i=1;i<=9;++i){
					board[y][x]=i;
					int k=Judge(x,y,i,board);
					if(k==0){
						Sel(x+1,y,kind,board,sudoku,total);
					}
					board[y][x]=0;
				}
			}else{
				Sel(x+1,y,kind,board,sudoku,total);
			}
		}else{
			Sel(0,y+1,kind,board,sudoku,total);
		}
	}else{
		++total;
		flag=0;
		if(kind==0){
			for(int i=0;i<9;++i){
				for(int j=0;j<9;++j){
					sudoku[i][j][0]=board[i][j];
				}
			}
		}
	}
}

int main(){
	int board[9][9],sudoku[9][9][2];
	int leve,op,input=35;
	for(int i=0;i<9;++i){
		for(int j=0;j<9;++j){
			board[i][j];
			sudoku[i][j][1]=0;
		}
	}
	Boundary();
	while(input>34 && input<56){
		total=0,flag=1;
		while(total==0){
			Sudoku(board);
			Sel(0,0,0,board,sudoku,total);
			gotoxy(50,22);
		}//生成符合数独规则的矩阵 
		Show(input,board,sudoku);//随机挖空 
		Sel(0,0,1,board,sudoku,total); 
		gotoxy(45,5);
		color(7);
		printf("解法:%d种             ",total-1); 
		gotoxy(54,9);
		int key=1;
		while(key==1){
			int h=0; 
			gotoxy(45,9);
			printf("操作 输入3位数:"); 
			scanf("%d",&op);			
			gotoxy(5+(op/100%10)*4,(op/10%10)*2+2);
			if(sudoku[op/10%10][op/100%10][1]==0 && op%10!=0 && op<999 && op/10%10!=9 && op/100%10!=9){
				board[op/10%10][op/100%10]=0;
				if(Flag(op/100%10,op/10%10,op%10,board)==true){
					board[op/10%10][op/100%10]=op%10;
					gotoxy(5+(op/100%10)*4,(op/10%10)*2+2);
					printf("%d",op%10);
					for(int i=0;i<9;++i){
						for(int j=0;j<9;++j){
							if(board[i][j]!=0){
								++h;
							}
						}
					}
					if(h==81){
						key=0;
						gotoxy(45,9);
						printf("你赢了!");
						Sleep(3000);
					}
				}else{
					color(12);
					gotoxy(45,11);
					printf("输入错误!");
					Sleep(500); 
				} 
			}else{
				if(op<999){
					color(7);
					gotoxy(45,11);
					printf("操作非法!");
					Sleep(500);
				}
			}
			if(op==999){
				key=0;
				gotoxy(45,9);
				printf("你输了!现在显示其中一种解法!");
				for(int i=0;i<9;++i){
					for(int j=0;j<9;++j){
						if(sudoku[i][j][1]==0){
							Sleep(100);
							gotoxy(5+j*4,i*2+2);
							printf("%d",sudoku[i][j][0]);
						}
						board[i][j]=0;
						sudoku[i][j][0]=0;
						sudoku[i][j][1]=0; 
					}
				} 
			}
			color(7);
			gotoxy(45,9);
			printf("操作:                          ");
			gotoxy(45,11);
			printf("                  ");
		}
		gotoxy(45,11);
		printf("输入35~55之间的数字开始新游戏,或按其他键退出:");
		scanf("%d",&input);
		gotoxy(45,11);
		printf("                                                           ");
	} 
	gotoxy(0,20);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_40636117/article/details/82461987