您的位置:首页 > 编程语言

【个人项目】数独项目完整代码

2018-04-09 21:12 369 查看

数独游戏代码实现

       具体思路和说明见前几篇博客。
主函数(对外窗口):
main.cpp:
       主要实现从命令行读入指令,然后判断输入是否正确,正确则执行指令。/// <summary>
/// 功能:1.实现向文件输出1 ~ 100 0000个数独终局 (本项目设计最多生成 290 3040 个不重复的数独终局)
/// 2.实现从指定文件读取数独残局,解数独
/// </summary>
/// <author> 李林峰 </author>
/// <student_number> 1120161918 </student_number>

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include "define.h"
#include "initial.h"
#include "create.h"
#include "solve.h"

int main(int argc,char* argv[])
{
if( strcmp(argv[1],"-c") == 0 ) {
int num = change_num(argv[2]);

if(num==-1)
printf("\nWrong: Please input correctly!\n");
else if(num==-2)
printf("\nWrong: The number you entered exceeds the maximun range!\n");
else if(num==-3)
printf("\nWrong: The number you entered exceeds the maximun range!\n");
else if(num==0)
printf("\nWrong: Please input correctly!\n");
else {
if(origin[0][0]==0) {
initial_origin();
}

int examine = create_sudoku(num);

if(examine==-1)
printf("\nProgram Execution Error: Wrong on creation of the end of sudoku.\n");
}
}
else if( strcmp(argv[1],"-s") == 0 ) {
FILE *fp;
fp = fopen(argv[2],"r");
if( fp == NULL ) {
printf("\nWrong: File open failed.\n");
}
solve_sudoku(fp);
}

return 0;
}数据结构定义模块:
define.h#ifndef __DIFINE__
#define __DIFINE__

// 数独边界大小
#define SIZE 9

// 解数独棋盘结构
typedef struct {
int map[SIZE][SIZE]; // 待解数独盘面
int row[SIZE]; // 行
int col[SIZE]; // 列
int grid[SIZE]; // 九宫格
} BOARD;

// 输入的字符变为数字
int change_num(char s[]);

// 输出写入文件
void print(FILE* fp,BOARD board,int flag);

#endifdefine.cpp
       注意change_num()函数,在本项目设计中,最大能够构造2903040种不同数独终局,作为1e6到2903040之间单独返回一个值为后续扩大输入范围提供了更改窗口。/// <summary>
/// 本文件实现基本输入输出
/// </summary>

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include "define.h"

/// <summary> 输入的字符变为数字 </summary>
/// <return>
/// -1:不是数字 ; -2: 数字太大 ; -3:输入数字超过题目要求但可以执行 ; else 返回字符串代表的数字
/// </return>
int change_num(char s[])
{
int len = strlen(s);

for(int i=0;i<len;i++) {
if( isdigit(s[i]) ) {
continue;
}
else return -1; // 返回 -1 表示不是数字
}

if(len>8) return -2; // 返回-2 表示数字太大

int num = 0;
for(int i=0;i<len;i++) {
num = num*10 + s[i] - '0' ;
}

if(num>2903040) return -2;// 返回-2 表示数字太大
if(num>1000000&&num<=2903040) return -3;//// 返回-3 表示输入数字超过题目要求但可以执行
return num;
}

/// <summary> 输出写入文件 </summary>
/// <param name="fp"> 指向写入的文件 </param>
/// <param name="board"> 需要打印的数独局面 </param>
/// <param name="flag"> 标志(值为0不打印空行) </param>
void print(FILE* fp,BOARD board,int flag)
{
if(flag!=0)
fputc('\n',fp);
for(int i=0;i<9;i++) {
for(int j=0;j<9;j++) {
fprintf(fp,"%d%c",board.map[i][j],j==8?'\n':' ');
}
}
}初始化模块(实现求取全排列功能)
initial.h
#ifndef __INITIAL__
#define __INITIAL__

// 生成数独终局构造初始数据
extern int origin[40320][9];

// 初始化全排列数组 origin[40320][9]
void initial_origin();

#endifinitial.cpp

       主要是求取全排列,为创建数独终局提供初始局面。/// <summary>
/// 本文件对外提供 initial_origin()函数,为 origin 数组初始化
/// 本文件对外提供 origin数组,供生成 290 3040 个不重复的数独终局
/// </summary>

#include "initial.h"

int origin[40320][9] = { 0 }; // 存放2~8全排列,第1位根据学号要求存放1
int init[9] = { 1, 2, 3, 4, 5, 6, 7, 8, 9};

int count = 0;

/// <summary> 实现交换两个数 </summary>
inline void swap(int& a,int& b)
{
int temp=a; a=b; b=temp;
}

/// <summary> 递归求取全排列 </summary>
void permutation(int offset)
{
if(offset == 8) {
for(int i=0;i<9;i++) {
origin[count][i]=init[i];
}
count++;
return;
}
for(int i=offset;i<9;i++) {
swap(init[i], init[offset]);
permutation(offset + 1);
swap(init[i], init[offset]);
}
}

/// <summary> 初始化全排列数组 </summary>
void initial_origin()
{
count = 0;
permutation(1);
}生成终局模块
create.h#ifndef __CREATE__
#define __CREATE__

// 生成n个数独终局,返回值-1表明程序执行错误
int create_sudoku(int n);

#endifcreate.cpp/// <summary>
/// 本文件对外提供 create_sudoku(int n)函数,实现构造 n 个不重复数独终局,并输出。
/// </summary>
/// <comment>
/// 学号:1120161918
/// (1+8)%9+1 = 1
/// </comment>

#include <stdio.h>
#include "define.h"
#include "initial.h"
#include "create.h"

int shift[9] = { 0, 6, 3, 8, 5, 2, 7, 4, 1 };

int generate_1[2][3] = { {0,1,2}, {0,2,1} };
int generate_2[6][3] = { {3,4,5}, {3,5,4}, {4,3,5}, {4,5,3}, {5,3,4}, {5,4,3} };
int generate_3[6][3] = { {6,7,8}, {6,8,7}, {7,6,8}, {7,8,6}, {8,6,7}, {8,7,6} };

inline void fill(int m,int n,int l,BOARD &backup,BOARD &board)
{
for(int i=0;i<3;i++) {
for(int j=0;j<9;j++) {
board.map[i][j] = backup.map[ generate_1[m][i] ][j] ;
}
}
for(int i=3;i<6;i++) {
for(int j=0;j<9;j++) {
board.map[i][j] = backup.map[ generate_2
[i-3] ][j] ;
}
}
for(int i=6;i<9;i++) {
for(int j=0;j<9;j++) {
board.map[i][j] = backup.map[ generate_3[l][i-6] ][j] ;
}
}
}

/// <summary> 生成n个数独终局 </summary>
/// <param name="n"> 需求生成数独终局的数量 </param>
/// <return> 1:正常生成数独终局; -1:程序执行出错 </return>
int create_sudoku(int n)
{
FILE *fp;
fp = fopen("sudoku.txt","w");
int count=0; // 计数,第一次输出不输出空行
BOARD board;

for(int i=0;i<40320;i++) {
BOARD backup;
for(int k=0;k<9;k++) {
backup.map[0][k] = origin[i][k];
backup.row[k] = 1022;
backup.col[k] = 1022;
backup.grid[k] = 1022;
}

for(int j=1;j<9;j++) {
for(int k=0;k<9;k++) {
backup.map[j][k] = backup.map[0][ (k+shift[j])%9 ] ;
}
}

board = backup; // 输出初始构造
for(int j=0;j<6;j++) {
for(int k=0;k<6;k++) {
// 1~3行填充方案1
if(count<n) {
fill(0,j,k,backup,board);
print(fp,board,count);
count++;
}
else if(count==n) {
fclose(fp);
return 1; // 正常完成命令
}
else if(count>=n) {
fclose(fp);
return -1; // 报错:程序执行错误
}
// 1~3行填充方案2
if(count<n) {
fill(1,j,k,backup,board);
print(fp,board,count);
count++;
}
else if(count==n) {
fclose(fp);
return 1; // 正常完成命令
}
else if(count>=n) {
fclose(fp);
return -1; // 报错:程序执行错误
}
}
}
}
fclose(fp);
return -1; // 报错:输入n已经超过能生成的最大个数,并且已经生成 290 3040组
}解数独模块
solve.h#ifndef __SOLVE__
#define __SOLVE__

// 最终返回的board已经填写好数独结果
void solution(BOARD &board,BOARD &backup);

// 将fp读取的题目解出
void solve_sudoku(FILE *fp);

#endifsolve.cpp/// <summary>
/// 本文件实现解数独
/// </summary>

#include <stdio.h>
#include <string.h>
#include "define.h"
#include "solve.h"

/// <summary> 在数独矩阵中找到第一个待解元素的序号,找不到返回-1 </summary>
inline int find(BOARD &board)
{
for(int i=0;i<SIZE;i++) {
for(int j=0;j<SIZE;j++) {
if( board.map[i][j]==0 ) {
return i*SIZE+j;
}
}
}
return -1;
}

inline bool set(BOARD &board,const int loc,const int num)
{
int r = loc/SIZE;
int c = loc%SIZE;
int g = (r/3)*3 + c/3 ;

bool valid = board.map[r][c] == 0
&& ( board.row[r] & (1<<num) ) ==0
&& ( board.col[c] & (1<<num) ) ==0
&& ( board.grid[g] & (1<<num) ) ==0 ;

if(!valid) {
return false;
}

board.map[r][c] = num;
board.row[r] |= (1<<num) ;
board.col[c] |= (1<<num) ;
board.grid[g] |= (1<<num) ;

return true;
}

inline void unset(BOARD &board,const int loc)
{
int r = loc/SIZE;
int c = loc%SIZE;
int g = (r/3)*3 + c/3 ;

int num = board.map[r][c];

board.row[r] ^= (1<<num) ;
board.col[c] ^= (1<<num) ;
board.grid[g] ^= (1<<num) ;

board.map[r][c] = 0;
}

/// <summary> 最终返回的board已经填写好数独结果 </summary>
void solution(BOARD &board,BOARD &backup)
{
int loc = find(backup);

if(loc<0) {
board = backup;
return ;
}

for(int i=1;i<=9;i++) {
if( set(backup,loc,i) ) {
solution(board,backup);
unset(backup,loc);
}
}
}

inline void cal_board(BOARD &board)
{
memset(board.row,0,sizeof(board.row));
memset(board.col,0,sizeof(board.col));
memset(board.grid,0,sizeof(board.grid));

int num=0;
for(int i=0;i<9;i++) {
for(int j=0;j<9;j++) {
if(board.map[i][j]!=0) {
num = board.map[i][j];
int g = (i/3)*3 + j/3 ;
board.row[i] |= (1<<num) ;
board.col[j] |= (1<<num) ;
board.grid[g] |= (1<<num) ;
}
}
}
}

/// <summary> 将fp读取的题目解出 </summary>
void solve_sudoku(FILE *fp)
{
FILE *fp_w;
fp_w = fopen("sudoku.txt","w");

int flag=0;
BOARD board;

int tmp;
while( ~fscanf(fp,"%d",&tmp) ) {
board.map[0][0] = tmp;
for(int j=1;j<9;j++) {
fscanf(fp,"%d",&board.map[0][j]);
}
for(int i=1;i<9;i++) {
for(int j=0;j<9;j++) {
fscanf(fp,"%d",&board.map[i][j]);
}
}

cal_board(board);

BOARD backup = board;
solution(board,backup);
print(fp_w,board,flag);
flag=1;
}

fclose(fp);
fclose(fp_w);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: