C语言学习 数独游戏

摘要:花了1周多时间学习了C语言,开始练手写解数独游戏的程序。

作者:乌龙哈里
时间:2015-11-22
平台:Window7 64bit,TCC 0.9.26(x86-64 Win64)

参考:

章节:

正文:

原来也用C#和Go语言写过,主要思路是暴力撞大运破解。思路什么的在程序了都注释了,不多说了。可能是没用什么先进的算法,感觉C解题速度和C#差不多(除了C#第一次运行之外),基本上出来一个数独表都不用1秒。

附完整程序:

  1 /***************************************************
  2 *数独表
  3 *作者:乌龙哈里
  4 *编写时间:2015-11-21
  5 *修改时间:2015-11-22
  6 *思路:
  7 1、每个表元素结构属性有:
  8    值Value,回溯标志IsBack,可选值数量Remain,可选值数组Selection[9];
  9 2、根据规则,把数独表看成27个,每个含有9个元素的行列块组;
 10    为了循环方便,分成:0-8:行组,9-17:列组,18-26:块组;
 11 3、找出最少要填空格的行列块组,开始填写。填完这个再找下个最少的;
 12 4、填写时先从行列块组中挑出剩下可填写的数字,从中随机找个值;
 13 5、没有可选的时候,开始从回溯表中回到上一步,
 14    回溯时如果可选值数量大于1时,则抛弃先前所填值,用另外的
 15    值来尝试。
 16 ****************************************************/
 17
 18 #include <stdio.h>
 19 #include <stdlib.h>
 20
 21 /*
 22 *把表看成27组,每组9个元素,
 23 *    0-8:行组,9-17:列组,18-26:块组
 24     行内序号:从左到右 012345678
 25     列内序号:从上到下 012345678
 26     块内序号:012
 27               345
 28               678
 29 *GetRcb():根据index计算出行列块
 30 *参数:index: 序号 0-81,flag:0-2,0-行,1-列,2-块
 31 *
 32 *GetNum():根据行列块组rcb和块内序号index计算出在数独表中的序号
 33 */
 34 int GetRcb(int index,int flag){
 35     int result=-1;
 36     switch(flag){
 37         case 0:
 38             result=index / 9;
 39             break;
 40         case 1:
 41             result=index % 9+9;
 42             break;
 43         case 2:
 44             result=index/9/3*3+index%9/3+18;
 45             break;
 46     }
 47     return result;
 48 }
 49
 50 int GetNum(int rcb,int index){
 51     int result=-1;
 52     int flag=rcb/9;
 53     switch(flag){
 54         case 0:
 55             result=rcb*9+index;
 56             break;
 57         case 1:
 58             result=rcb-9+index*9;
 59             break;
 60         case 2:
 61             result=(rcb-18)/3*27+(rcb-18)%3*3+index/3*9+index%3;
 62             break;
 63     }
 64     return result;
 65 }
 66
 67 //定义:数独表、表内元素结构、回溯表,回溯只记录30步
 68 typedef signed char byte;
 69 typedef char bool;
 70
 71 #define true 1
 72 #define false 0
 73
 74 byte SudokuTable[81]={0};
 75
 76 #define STEP 30
 77 int RecallTable[STEP]={-1};
 78
 79 typedef struct element{
 80     byte Value;
 81     bool IsBack;
 82     byte Remain;
 83     byte Selection[9];
 84 }Sudoku;
 85
 86 Sudoku *sdk;
 87
 88 /*
 89 初始化数独元素:
 90 */
 91 void InitSudoku(void){
 92     sdk=(Sudoku*)malloc(81*sizeof(Sudoku));
 93     for(int i=0;i<81;i++){
 94         sdk[i].Value=SudokuTable[i];
 95         sdk[i].IsBack=false;
 96         sdk[i].Remain=9;
 97     }
 98 }
 99
100 //查找最少空的行列块,用意:从这个开始填空
101 int GetFirstRcb(void){
102     int result=0;
103     int lessNum=9;
104     int n;
105     for(int i=0;i<27;i++){
106         n=9;
107         for (int j=0;j<9;j++){
108             if(sdk[GetNum(i,j)].Value>0){
109                 n--;
110             }
111         }
112         if(n>0 && n<lessNum){
113             result=i;
114             lessNum=n;
115         }
116     }
117     return result;
118 }
119
120 //整理可选值数组,把0值丢后面,可选值放前面,返回可选数
121 byte Arrange(int index){
122     byte result=0;
123     for(int i=0;i<9;i++){
124         if(sdk[index].Selection[i]>0){
125             sdk[index].Selection[result]=sdk[index].Selection[i];
126             if(i!=result){sdk[index].Selection[i]=0;}
127             result++;
128         }
129     }
130     return result;
131 }
132 /*
133 *设置可填写数字数组:
134 遍历元素所属的行列块中元素,在Selection数组中把用过的值设成0;
135 */
136 void SetSelection(int index){
137     for(int i=0;i<9;i++){sdk[index].Selection[i]=i+1;}
138     int rcb;
139     int n;
140     for(int i=0;i<3;i++){
141         rcb=GetRcb(index,i);
142         for(int j=0;j<9;j++){
143             n=GetNum(rcb,j);
144             if(sdk[n].Value>0){
145                 sdk[index].Selection[sdk[n].Value-1]=0;
146             }
147         }
148     }
149     sdk[index].Remain=Arrange(index);
150 }
151
152 //随机选出可填写值
153 byte GetValue(int index){
154     byte result=0;
155     srand((unsigned int)time(0));
156     int n=rand()%sdk[index].Remain;
157     result=sdk[index].Selection[n];
158     sdk[index].Selection[n]=0;
159     sdk[index].Remain=Arrange(index);
160     return result;
161 }
162
163 /*
164 回溯,如果回溯表内没有记录,返回-1
165 如果可选值数量大于0的,则把回溯标记设成true
166 */
167 int Recall(void){
168     int index;
169     for(int i=0;i<STEP;i++){
170         if(RecallTable[i]>-1){
171             index=RecallTable[i];
172             sdk[index].Value=0;
173             RecallTable[i]=-1;
174             if(sdk[index].Remain==0){
175                 sdk[index].IsBack=false;
176             }
177             else{
178                 sdk[index].IsBack=true;
179                 return index;
180             }
181         }
182     }
183     return -1;
184 }
185 /*
186 填写回溯表
187 从后往前填写,满了就移动
188 */
189 void WriteRecallTable(int index){
190     if(RecallTable[0]>-1){
191         for(int i=STEP-1;i>0;i--){
192             RecallTable[i]=RecallTable[i-1];
193         }
194         RecallTable[0]=index;
195     }
196     else{
197         for(int i=0;i<STEP;i++){
198             if(RecallTable[i]>-1){
199                 RecallTable[i-1]=index;
200                 break;
201             }
202         }
203     }
204 }
205 /*
206 根据行列块分组来填写元素。
207 如果是回溯回来的,则抛弃掉现有的值,即不SetSelection(),
208 因为GetValue()选出值后会把所填写的值从可选值数组中除去,
209 而SetSelection()是根据所有行列块组的元素值选出剩下的可填值,
210 包括了上次行不通的值。
211 */
212 bool WriteRcb(int rcb){
213     int index;
214     for(int i=0;i<9;i++){
215         index=GetNum(rcb,i);
216         if(sdk[index].Value==0){
217             if(sdk[index].IsBack==false){
218                 SetSelection(index);
219             }
220             if (sdk[index].Remain==0){
221                 return false;
222             }
223             sdk[index].Value=GetValue(index);
224             sdk[index].IsBack=true;
225             WriteRecallTable(index);
226         }
227     }
228     return true;
229 }
230 //判断填完没有
231 bool Completed(void){
232     for(int i=80;i>-1;i--){
233         if(sdk[i].Value==0) {
234             return false;
235         }
236     }
237     return true;
238 }
239 //填写全表
240 void FillTable(void){
241     int loop=1000;
242     int firstRcb=GetFirstRcb();
243
244     while(loop>0 && Completed()==false){
245         if(WriteRcb(firstRcb)==false){
246             if(Recall()==-1){
247                 printf("Unlucky,cannot solve!\n");
248                 break;
249             }
250         }
251         firstRcb=GetFirstRcb();
252         loop--;
253     }
254 }
255
256 /*************************************
257 *各种输出显示模块
258 **************************************/
259 // //按行列块分组显示元素序号
260 // void DisplayRcb(void){
261 //     for (int i = 0; i <27;i++){
262 //         if(i%9==0){printf("\n");}
263 //         printf("%2d: ", i%9);
264 //         for (int j = 0; j <9; j++){
265 //             printf("%2d ",GetNum(i,j));
266 //         }
267 //         printf("\n");
268 //     }
269 // }
270 //显示所有数独表元素
271 void Display(void){
272     for(int i=0;i<9;i++){
273         if(i==3 || i==6){
274             printf("------+------+------\n");
275         }
276         for(int j=0;j<9;j++){
277             if(j==3 || j==6){ printf("|");}
278             printf("%2d",sdk[i*9+j].Value);
279         }
280         printf("\n");
281     }
282 }
283 // //显示元素可选数字
284 // void DisplayRemain(int index){
285 //     printf("element %d remain %d selecttion: ",index,sdk[index].Remain);
286 //     for(int i=0;i<9;i++){
287 //         printf("%d ",sdk[index].Selection[i]);
288 //     }
289 //     printf("\n");
290 // }
291
292 /********************************
293 *Main
294 *********************************/
295 int main(void){
296     InitSudoku();
297     FillTable();
298     Display();
299     free(sdk);
300     return 0;
301 }
时间: 2024-11-08 18:23:30

C语言学习 数独游戏的相关文章

go例子(二) 使用go语言实现数独游戏

例子托管于github example.go package main import ( "./sudoku" ) func main() { //var smap sudoku.Sudomap //smap = make([]byte,9) //for i:= 0; i<len(smap);i++{ //  smap[i] = make (byte,9) //} smap := sudoku.Sudomap{ {5, 3, 0, 0, 7, 0, 0, 0, 0}, {6, 0

C语言/C+游戏编程学习之简单 DLL 劫持,就是这么任性

C语言面向过程编程的语言:C++面向对象编程的语言. 两者有本质的区别,其实是完全不同的两种语言,只不过C++兼容C语言而已. 其中C++则一般看作是对C语言的扩展.因为C语言没有面向对象的语法结构,而当时业界又迫切需要面向对象的编程特性,所以贝尔实验室的开发者就为C语言添加了面向对象的结构.现在C++已经不只是C语言的扩展了,它已经完全可以被看作一种新的编程语言.虽然C语言的特性以及库函数仍然被C++支持,不过C++拥有自己的独立的类库体系,功能相当强大. C/C++的优点在于与底层比较接近,

C语言学习教程:搬箱子游戏开发源码分享

C语言学习教程:搬箱子游戏开发源码分享,推箱子游戏的规则非常简单,就是用尽量少的推动或移动把所有箱子都推到目标点上.箱子只能推动而不能拉动:一次只能推动一个箱子. 源码截图,由于有点多就不一 一截图了,完整的可以加我交流裙免费领取,这里推荐一下我建的C/C++语言学习交流秋秋裙,前三位是:110,中间三位是:355,最后三位是:025,为了让学习变得轻松,高效!给大家分享一套教学资源,帮助大家在成为C/C++语言开发高手的道路上披荆斩棘,群内每晚八点免费直播授课,讲解C/C++语言案例,同时还有

【QT学习】数独游戏

前几天刷leetcode刷到一题,讲sudokuSolver,写完感觉很有意思,遂想做一个数独游戏,百度了一下如何自动生成题库,参考某位大神安卓下的实现思路,自己做了一套文字版的数独游戏,后来想乘机会学一下QT于是完成了图形界面的数独.先mark一下之后从数独解题,数独出题,QT图形界面来讲讲自己的心得体会. 界面写完是这样的.也算有点小成就感了. 游戏链接+QT运行动态库 http://pan.baidu.com/s/1kTu5alh

100个vc/c/c++语言学习网站/学习教程

软件: http://www.icodeguru.com/fav/soft.htm  教程: C函数实例参考手册 http://www.icodeguru.com/CPP/CExample/ C\C++实例参考手册 http://www.icodeguru.com/CPP/cppExample/ Win32Api实例参考手册 http://www.icodeguru.com/VC%26MFC/Win32ApiExample/ MFC实例参考手册 http://www.icodeguru.com/

《数独游戏的设计与实现》

数独游戏的规则很简单,只需要在空格初填入1~9的数字,并保证每个数字在每个九宫格内只能出现一次,且每个数字在每一行.每一列也只能出现一次,而一半的游戏过程是系统随机生成一个棋局,然后玩家需要在空白处填上相应的数字使其满足游戏的规则. 完成过程:绘制一个9*9的九宫格,设计了简单和困难两种难度,设置其中几个格子的数字,使其形成一个数独游戏,然后通过X,Y轴获取用户输入的数字. 设计与实现思想:运用遍历算法完成九宫格的获取 遇到的问题与解决方法:因为之前没有学习过安卓,从现在才开始慢慢接触,只能上网

【C语言学习】《C Primer Plus》第1章 概览

学习总结 1.C语言于1972年由贝尔实验室的Dennis Ritchie在与Ken Thompson一起设计UNIX操作系统的时候开发的.的的设计构想来源于Ken Thompson的B语言.Anyway,它70世纪所需产物,存在即合理. 2.因为UNIX&Linux都是基于C语言开发的,所以,C无所不在:游戏.操作系统.计算机语言.嵌入式.应用…… 3.C实现顺序:源码(.c)→编译器→目标代码(.obj)→链接器(代码库+启动代码)→可执行文件(Linux默认是.out文件,win是.exe

c语言学习基础:[1]开发工具介绍

标签:c语言 1 2 3 4 分步阅读 学习编程语言的童鞋们一开始接触到的最多的估计就是C语言了,其次才是什么java.c++等,可以说学习c语言是我们走向编程世界的一座桥梁,学好它,对于我们学习和研究计算机有着至关重要的作用,同时也是我们开发各类计算机应用软件.开发游戏等必要的前提条件.本小节主要向大家介绍一下开发C语言的工具,让大家如何从基础开始学习. 工具/原料 计算机 开发工具 方法/步骤 开发C语言,有很多开发工具,其中比较老一点的就是VC6.0,现在的大学计算机二级等级考试依然用的是

【WEB前端开发】新手入门:html语言学习的7个秘诀

WEB前端开发学习过程中,作为一个新手入门级别的菜鸟你,要如何系统的学习呢 ? 本期为介绍其一: html语言学习的7个秘诀 也就是说如何学习HTML语言. 新手们有一个常见的错误就是犹豫于判断哪种编程语言是做好的.最该先学的. 我们有很多的选择,但你不能说那种语言"最好".我们应该理解:说到底,什么语言并不重要. 重要的是理解数据结构.控制逻辑和设计模式. 任何一种语言-甚至一种简单的脚本语言-都会具有所有编程语言都共有的各种特征,也就是说各种语言是贯通的. 我编程使用Pascal,