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

C语言学习 数独游戏

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

参考:

章节:

正文:

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

附完整程序:

 /***************************************************
*数独表
*作者:乌龙哈里
*编写时间:2015-11-21
*修改时间:2015-11-22
*思路:
1、每个表元素结构属性有:
值Value,回溯标志IsBack,可选值数量Remain,可选值数组Selection[9];
2、根据规则,把数独表看成27个,每个含有9个元素的行列块组;
为了循环方便,分成:0-8:行组,9-17:列组,18-26:块组;
3、找出最少要填空格的行列块组,开始填写。填完这个再找下个最少的;
4、填写时先从行列块组中挑出剩下可填写的数字,从中随机找个值;
5、没有可选的时候,开始从回溯表中回到上一步,
回溯时如果可选值数量大于1时,则抛弃先前所填值,用另外的
值来尝试。
****************************************************/ #include <stdio.h>
#include <stdlib.h> /*
*把表看成27组,每组9个元素,
* 0-8:行组,9-17:列组,18-26:块组
行内序号:从左到右 012345678
列内序号:从上到下 012345678
块内序号:012
345
678
*GetRcb():根据index计算出行列块
*参数:index: 序号 0-81,flag:0-2,0-行,1-列,2-块
*
*GetNum():根据行列块组rcb和块内序号index计算出在数独表中的序号
*/
int GetRcb(int index,int flag){
int result=-;
switch(flag){
case :
result=index / ;
break;
case :
result=index % +;
break;
case :
result=index//*+index%/+;
break;
}
return result;
} int GetNum(int rcb,int index){
int result=-;
int flag=rcb/;
switch(flag){
case :
result=rcb*+index;
break;
case :
result=rcb-+index*;
break;
case :
result=(rcb-)/*+(rcb-)%*+index/*+index%;
break;
}
return result;
} //定义:数独表、表内元素结构、回溯表,回溯只记录30步
typedef signed char byte;
typedef char bool; #define true 1
#define false 0 byte SudokuTable[]={}; #define STEP 30
int RecallTable[STEP]={-}; typedef struct element{
byte Value;
bool IsBack;
byte Remain;
byte Selection[];
}Sudoku; Sudoku *sdk; /*
初始化数独元素:
*/
void InitSudoku(void){
sdk=(Sudoku*)malloc(*sizeof(Sudoku));
for(int i=;i<;i++){
sdk[i].Value=SudokuTable[i];
sdk[i].IsBack=false;
sdk[i].Remain=;
}
} //查找最少空的行列块,用意:从这个开始填空
int GetFirstRcb(void){
int result=;
int lessNum=;
int n;
for(int i=;i<;i++){
n=;
for (int j=;j<;j++){
if(sdk[GetNum(i,j)].Value>){
n--;
}
}
if(n> && n<lessNum){
result=i;
lessNum=n;
}
}
return result;
} //整理可选值数组,把0值丢后面,可选值放前面,返回可选数
byte Arrange(int index){
byte result=;
for(int i=;i<;i++){
if(sdk[index].Selection[i]>){
sdk[index].Selection[result]=sdk[index].Selection[i];
if(i!=result){sdk[index].Selection[i]=;}
result++;
}
}
return result;
}
/*
*设置可填写数字数组:
遍历元素所属的行列块中元素,在Selection数组中把用过的值设成0;
*/
void SetSelection(int index){
for(int i=;i<;i++){sdk[index].Selection[i]=i+;}
int rcb;
int n;
for(int i=;i<;i++){
rcb=GetRcb(index,i);
for(int j=;j<;j++){
n=GetNum(rcb,j);
if(sdk[n].Value>){
sdk[index].Selection[sdk[n].Value-]=;
}
}
}
sdk[index].Remain=Arrange(index);
} //随机选出可填写值
byte GetValue(int index){
byte result=;
srand((unsigned int)time());
int n=rand()%sdk[index].Remain;
result=sdk[index].Selection[n];
sdk[index].Selection[n]=;
sdk[index].Remain=Arrange(index);
return result;
} /*
回溯,如果回溯表内没有记录,返回-1
如果可选值数量大于0的,则把回溯标记设成true
*/
int Recall(void){
int index;
for(int i=;i<STEP;i++){
if(RecallTable[i]>-){
index=RecallTable[i];
sdk[index].Value=;
RecallTable[i]=-;
if(sdk[index].Remain==){
sdk[index].IsBack=false;
}
else{
sdk[index].IsBack=true;
return index;
}
}
}
return -;
}
/*
填写回溯表
从后往前填写,满了就移动
*/
void WriteRecallTable(int index){
if(RecallTable[]>-){
for(int i=STEP-;i>;i--){
RecallTable[i]=RecallTable[i-];
}
RecallTable[]=index;
}
else{
for(int i=;i<STEP;i++){
if(RecallTable[i]>-){
RecallTable[i-]=index;
break;
}
}
}
}
/*
根据行列块分组来填写元素。
如果是回溯回来的,则抛弃掉现有的值,即不SetSelection(),
因为GetValue()选出值后会把所填写的值从可选值数组中除去,
而SetSelection()是根据所有行列块组的元素值选出剩下的可填值,
包括了上次行不通的值。
*/
bool WriteRcb(int rcb){
int index;
for(int i=;i<;i++){
index=GetNum(rcb,i);
if(sdk[index].Value==){
if(sdk[index].IsBack==false){
SetSelection(index);
}
if (sdk[index].Remain==){
return false;
}
sdk[index].Value=GetValue(index);
sdk[index].IsBack=true;
WriteRecallTable(index);
}
}
return true;
}
//判断填完没有
bool Completed(void){
for(int i=;i>-;i--){
if(sdk[i].Value==) {
return false;
}
}
return true;
}
//填写全表
void FillTable(void){
int loop=;
int firstRcb=GetFirstRcb(); while(loop> && Completed()==false){
if(WriteRcb(firstRcb)==false){
if(Recall()==-){
printf("Unlucky,cannot solve!\n");
break;
}
}
firstRcb=GetFirstRcb();
loop--;
}
} /*************************************
*各种输出显示模块
**************************************/
// //按行列块分组显示元素序号
// void DisplayRcb(void){
// for (int i = 0; i <27;i++){
// if(i%9==0){printf("\n");}
// printf("%2d: ", i%9);
// for (int j = 0; j <9; j++){
// printf("%2d ",GetNum(i,j));
// }
// printf("\n");
// }
// }
//显示所有数独表元素
void Display(void){
for(int i=;i<;i++){
if(i== || i==){
printf("------+------+------\n");
}
for(int j=;j<;j++){
if(j== || j==){ printf("|");}
printf("%2d",sdk[i*+j].Value);
}
printf("\n");
}
}
// //显示元素可选数字
// void DisplayRemain(int index){
// printf("element %d remain %d selecttion: ",index,sdk[index].Remain);
// for(int i=0;i<9;i++){
// printf("%d ",sdk[index].Selection[i]);
// }
// printf("\n");
// } /********************************
*Main
*********************************/
int main(void){
InitSudoku();
FillTable();
Display();
free(sdk);
return ;
}

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

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

    例子托管于github example.go package main import (     "./sudoku" ) func main() {     //var smap ...

  2. 经典数独游戏+数独求解器—纯C语言实现

    "心常乐数独小游戏"(下面简称"本软件")是一款windows平台下的数独游戏软件. 本软件是开源.免费软件. 本软件使用纯C语言编写,MinGW编译,NSIS ...

  3. 【QT学习】数独游戏

    前几天刷leetcode刷到一题,讲sudokuSolver,写完感觉很有意思,遂想做一个数独游戏,百度了一下如何自动生成题库,参考某位大神安卓下的实现思路,自己做了一套文字版的数独游戏,后来想乘机会 ...

  4. 用html5 canvas和JS写个数独游戏

    为啥要写这个游戏? 因为我儿子二年级数字下册最后一章讲到了数独.他想玩儿. 因为我也想玩有提示功能的数独. 因为我也正想决定要把HTML5和JS搞搞熟.熟悉一个编程平台,最好的办法,就是了解其原理与思 ...

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

    学习总结 1.C语言于1972年由贝尔实验室的Dennis Ritchie在与Ken Thompson一起设计UNIX操作系统的时候开发的.的的设计构想来源于Ken Thompson的B语言.Anyw ...

  6. 获取技能的成功经验和关于C语言学习的调查 2015528

    内容提要 你有什么技能比大多人(超过90%以上)更好?针对这个技能的获取你有什么成功的经验?与老师博客中的学习经验有什么共通之处? 有关C语言学习的调查 你是怎么学习C语言的?(作业,实验,教材,其他 ...

  7. 12天学好C语言——记录我的C语言学习之路(Day 7)

    12天学好C语言--记录我的C语言学习之路 Day 7: 昨天进行了一天的数组学习,今天大家可以先写几个昨天的程序热热身,回顾回顾,然后今天第一个新程序也是关于数组的,比较难,准备好就开始啦! //输 ...

  8. 郝斌老师C语言学习笔记(一)

    在给变量分配内存时,很可能这段内存存在以前其他程序使用留下的值.当使用VC编译器,若编译器发现没有给变量赋值而使用,就会返回一个以“85”开头的很大的数字(此时该段内存中为一个垃圾数,为了避免出现较常 ...

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

    标签:c语言 1 2 3 4 分步阅读 学习编程语言的童鞋们一开始接触到的最多的估计就是C语言了,其次才是什么java.c++等,可以说学习c语言是我们走向编程世界的一座桥梁,学好它,对于我们学习和研 ...

随机推荐

  1. 【Linux】CentOS 学习笔记之一(安装配制)

    如何打开命令窗口:应用程序>系统工具>终端 如何获取root 权限:在终端下,输入su 再输入密码(密码不显示在屏幕上) 如何将英文版转换成中文: [root@localhost ~]# ...

  2. TCMalloc小记

    周末抽空看了一下tcmalloc,了解了个大概.下面记录一下. 一. 原理 tcmalloc就是一个内存分配器,管理堆内存,主要影响malloc和free,用于降低频繁分配.释放内存造成的性能损耗,并 ...

  3. Android 布局详解

    Android 布局详解 1.重用布局 当一个布局文件被多处使用时,最好<include>标签来重用布局. 例如:workspace_screen.xml的布局文件,在另一个布局文件中被重 ...

  4. 框架基础:ajax设计方案(三)---集成ajax上传技术

    之前发布了ajax的通用解决方案,核心的ajax发布请求,以及集成了轮询.这次去外国网站逛逛,然后发现了ajax level2的上传文件,所以就有了把ajax的上传文件集成进去的想法,ajax方案的l ...

  5. Warensoft Stock Service Api客户端接口说明

    Warensoft Stock Service Api客户端接口说明 Warensoft Stock Service Api Client Reference 可使用环境(Available Envi ...

  6. java导出生成csv文件

    首先我们需要对csv文件有基础的认识,csv文件类似excel,可以使用excel打开,但是csv文件的本质是逗号分隔的,对比如下图: txt中显示: 修改文件后缀为csv后显示如下: 在java中我 ...

  7. 函数四种调用模式以及其中的this指向

    第一种:函数直接执行模式 function add(a,b){ console.log(this); return a+b; } add(10,20)//this===window 第二种:对象方法的 ...

  8. HDU1496 hash

    Equations Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total S ...

  9. Caffe安装过程错误处理方法

    1. 错误1: fatal error: caffe/proto/caffe.pb.h: No such file or directory 解决方法: You need to generate ca ...

  10. Linux进程通信——管道

    管道(pipe)本质上是一种文件,管道通信本质上是通过读写文件通信,但是管道解决了文件的两个问题:限制管道大小,解决read()调用文件结束问题. 管道一个环形的缓冲区,通过两个进程以生产者/消费者的 ...