简单的回溯法生成数独游戏

题目的原因:课程作业

这是一个课设的作业,对于理解回溯法很有用,所以把他码住。好久没敲这种代码了,边界居然调了半天,尴尬!

作业分析

作业描述:写个数独游戏,具有自动生成数独的功能和人工设定初始盘的功能,并且具有UI界面。

作业分析:
任务一:对于自动生成数独的功能,他的功能实现逻辑是:回溯生成一个数独,然后随机性让部分区域可视(对于玩家),当玩家选择提交时,再判断是否与生成的数独一致。
**任务二:**对于人工设定初始盘的功能,他和上面的自动生成有一些不同,当玩家选择手动生成数独并提交给后台之后,后台通过回溯法对提交的数组进行有解检查,如果无解返回“设定失败”,否则就找到所有的解,并保存全部。当玩家提交answer时,匹配每一种解。

任务一实现

回溯逻辑:对于一个用来保存生成的数组GAME,我们对其进行自上而下从左到右的填充,每次在当前位置(x,y)填充一个数字时,对该数字进行合法判断Iscorrect(x,y),如果不合法,进行回溯;合法进行下一次填充。

注:回溯法一定注意循环的逻辑,以及对continue一定要有充分的了解,不清楚的建议改为break,单步调试几次。特别注意,j=j-2而不是j=j-1!!!


private static int[][] game=new int [9][9];//储存数独
	
	

	private static int []ranNum= {1,2,3,4,5,6,7,8,9};//随机数生成数组
	private int[][] generateGame(){
		//初始化数独
		for(int i=0;i<9;i++)
			for(int j=0;j<9;j++)
				game[i][j]=0;
		
		
        //生成数独游戏的所有内容
		for(int i=0;i<9;i++) {
			int time=0;
			for(int j=0;j<9;j++) {
			   int tem=-1;
			   tem=getNum(time);
			   game[i][j]=tem;
			   if(tem==0) {
				   //说明已经不能再填充数字了
				   if(j>0) {
					   //当j不是第一列时,回溯上一列,由于j已经加了所以是j-2,而不是j--;特别注意
					  j=j-2;
					   continue;
				   }
				   else {
					  i--;
					  j=8;
					  continue;
				   }
				   
			   }
			   //如果可以获得数字,说明尝试的次数并没有用完
			   //对数字的合法性进行检查
			   if(Iscorrect(i,j)) {
				   time=0;
			   }
			   else {//填充失败,继续填充,应该
				   time++;
				   j--;
				   
			   }
				
				
			}
		
		}	
		
		return game;
	}

功能模块完善

1、自动生成随机数函数getNum(int time)
对于一个第一次,生成随机数的情况来说(time=0),我们初始化数组。
对于一个进行第九次挑选随机数的情况来说(time=9),我们已经没有数可以供他使用,所以返回不存在。
当进行数字生成时,我们只要保证生成随机数的下标是在MAX(未被使用的数字的下标),就可以实现,千万别忘了把已经使用过的数字和末位交换。
注:这个自己一定要写一下,我错了好几次!!


	//生成随机数独
	private int getNum(int time) {
		//第一次产生随机数
		
		Random r=new Random();
		if (time == 0) {
			for (int i = 0; i < 9; i++) {
				ranNum[i] = i + 1;
			}
		}
		// 第10次填充,表明该位置已经卡住,则返回0,由主程序处理退回
		if (time == 9) {
			return 0;
		}
		// 不是第一次填充
		// 生成随机数字,该数字是数组的下标,取数组num中该下标对应的数字为随机数字
//		int ranNum = (int) (Math.random() * (9 - time));//j2se
		int num=r.nextInt(9 - time);//j2me
		// 把数字放置在数组倒数第time个位置,
		int temp = ranNum[8 - time];
		ranNum[8 - time] = ranNum[num];
		ranNum[num] = temp;
		// 返回数字
		return ranNum[8-time];
	
	}

2.合法性判断(一定不要越界)
注:最好写个边界判断函数,我写代码时候越界了,没有输出就狠烦。

	private boolean Iscorrect(int i, int j) {
		// TODO Auto-generated method stub
		if(Checkline(i)&&CheckCol(j)&&CheckBox(i,j)) {
			return true;
		}
		
		else {
		
		return false;
	}
	}
	//检查3*3是否有重复
	private boolean CheckBox(int i, int j) {
	  //获取左上角进行遍历
		int sx=i/3*3;
		int sy=j/3*3;
		for(int m=0;m<8;m++) {
		//说明还没有填充
	    if(game[sx+m/3][sy+m%3]==0) {
	    	continue;
	    }
		for(int n=m+1;n<9;n++) {
			if(game[sx+m/3][sy+m%3]==game[sx+n/3][sy+n%3]) {
				//System.out.println("asdfasfa ");
				return false;
			}
				
		}
				
			
		}
		
		
		
		// TODO Auto-generated method stub
		return true;
	}
	
	
	//检查每行是否有重复
	private boolean Checkline(int i) {
       for(int m=0;m<8;m++) {//下标是8,开始是9越界了
    	   if(game[i][m]==0)
    		   continue;
    	   for(int n=m+1;n<9;n++) {
    		   //这行有重复的数字
    		   if(game[i][m]==game[i][n])
    	              return false;
    	   }
    	   
       }
		
		
		
		return true;
	}
	
	
	
	//检查每列是否有重复
	private boolean CheckCol(int j) {
		 for(int m=0;m<8;m++) {
	    	   if(game[m][j]==0)
	    		   continue;
	    	   for(int n=m+1;n<9;n++) {
	    		   //这一列有重复的数字
	    		   if(game[n][j]==game[m][j])
	    	              return false;
	    	   }
	    	   
	       }
		 return true;
	}
	

主程序部分

	public static void main(String[] args) {
		SDavi sd=new SDavi();
		for(int i=0;i<9;i++) {
		int tem=sd.getNum(i);
	//	System.out.println(tem);
		
		}
		
		int [][] game=new int[9][9];
		game=sd.generateGame();
		for(int i=0;i<9;i++) {
			for(int j=0;j<9;j++) {
				System.out.print(game[i][j]+" ");
			}
			System.out.println();
		}
	
		
	}

结语

之后,会尽快把任务二发上来,人机交互界面还不会,惨啊,Swing咱也没学过。

发布了3 篇原创文章 · 获赞 1 · 访问量 313

猜你喜欢

转载自blog.csdn.net/LUmayu/article/details/103910006