C数独回溯求解器预检函数
C sudoku backtracking solver precheck function
这是我第一次 post 在 Whosebug 上,所以如果我做错了什么,我深表歉意。我是 C 语言的新手,所以我确信我的代码在某些地方相当丑陋和骇人听闻,但是大部分代码都符合我的预期。我在使用 precheck
方法检查数独板之前遇到了问题,然后才开始通过我的求解器逻辑输入它。我正在重定向来自文本文件的输入,其中的字符串看起来像
4.....8.5.34.........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4......
4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.5......
4.....8.5.3..........7......2.....6.....8.4......1...x...6.3.7.5..2.....1.4......
417369825632158947958724316825437169791586432346912758289643571573291684164875293
417369825632158947958724316825437169791586432346912758289643.71573291684164875293
每个字符串(理想情况下)为 81 个字符,仅包含数字 1-9 和“.”。我把一个字符串解析成一个temp char数组,然后使用方法fillBoard
将temp数组中的chars转成一个2d int数组。完成后,我调用 precheck
方法。如果填满的棋盘没有通过行、列和框检查,precheck
方法 returns 一个,表示拼图不可解(意味着应该打印一条错误消息,并且程序应该移动到下一个字符串)。出于某种原因,我的 precheck
方法即使对于应该可解的字符串也返回一个。我不确定这是为什么。任何帮助,将不胜感激。谢谢。
#include <stdio.h>
#include <string.h>
int alphaError = 0;
struct Point findEmpty(int board[9][9]);
int usedInBox(int board[9][9], int boxStartRow, int boxStartCol, int num);
int positionSafe(int board[9][9], int row, int col, int num);
int usedInCol(int board[9][9], int col, int num);
int usedInRow(int board[9][9], int row, int num);
int solvePuzzle(int board[9][9]);
int precheck(int board[9][9]);
int main()
{
char c;
int charCount = 0;
int i = 0;
char tempStr[100000];
int board[9][9];
while((fscanf(stdin, "%c", &c)) != EOF)
{
printf("%c", c);
if(c != '\n')
{
if(isalpha(c))
{
alphaError = 1;
}
tempStr[i] = c;
i++;
charCount++;
}
else
{
if(charCount != 81 || alphaError == 1)
{
printf("Error\n\n");
i = 0;
charCount = 0;
alphaError = 0;
}
else
{
fillBoard(board, tempStr);
printBoard(board);
if(precheck(board) == 1)
{
printf("Error\n\n");
}
else
{
if(solvePuzzle(board) == 1)
{
printBoard(board);
}
else
{
printf("No solution\n\n");
}
}
i = 0;
charCount = 0;
}
}
}
return 0;
}
struct Point
{
int x;
int y;
} point;
struct Point findEmpty(int board[9][9])
{
struct Point point1;
point1.x = -1;
point1.y = -1;
int row, col;
for(row = 0; row < 9; row++)
{
for(col = 0; col < 9; col++)
{
if(board[row][col] == 0)
{
point1.x = col;
point1.y = row;
}
}
}
return point1;
}
int usedInBox(int board[9][9], int boxStartRow, int boxStartCol, int num)
{
int row, col;
for(row = 0; row < 3; row++)
{
for(col = 0; col < 3; col++)
{
if(board[row + boxStartRow][col + boxStartCol] == num)
{
return 1;
}
}
}
return 0;
}
int positionSafe(int board[9][9], int row, int col, int num)
{
if((usedInRow(board, row, num)) == 0 && (usedInCol(board, col, num)) == 0 && (usedInBox(board, (row-row%3), (col-col%3), num)) == 0)
{
return 1;
}
else
{
return 0;
}
}
int usedInCol(int board[9][9], int col, int num)
{
int row;
for(row = 0; row < 9; row++)
{
if(board[row][col] == num)
{
return 1;
}
}
return 0;
}
int usedInRow(int board[9][9], int row, int num)
{
int col;
for(col = 0; col < 9; col++)
{
if(board[row][col] == num)
{
return 1;
}
}
return 0;
}
int solvePuzzle(int board[9][9])
{
int num;
struct Point point2;
point2 = findEmpty(board);
if(point2.x == -1)
{
return 1;
}
for(num = 1; num <= 9; num++)
{
if(positionSafe(board, point2.y, point2.x, num) == 1)
{
board[point2.y][point2.x] = num;
if(solvePuzzle(board) == 1)
{
return 1;
}
board[point2.y][point2.x] = 0;
}
}
return 0;
}
void printBoard(int board[9][9])
{
int i, j;
for (i = 0; i < 9; i++)
{
for (j = 0; j < 9; j++)
{
printf("%d", board[i][j]);
}
}
printf("\n\n");
}
void fillBoard(int board[9][9], char tempStr[100000])
{
int i, j;
int k = 0;
for(i = 0; i < 9; i++)
{
for(j = 0; j < 9; j++)
{
if(tempStr[k] == '.')
{
board[i][j] = 0;
}
else
{
board[i][j] = (tempStr[k] - '0');
}
k++;
}
}
}
int precheck(int board[9][9])
{
int i, j, num;
for(i = 0; i < 9; i++)
{
for(j = 0; j < 9; j++)
{
if(board[i][j] != 0)
{
num = board[i][j];
if(positionSafe(board, i, j, num) == 0)
{
return 1;
}
}
}
}
return 0;
}
所以您在已经填满的板上使用 precheck
?这可能是问题所在,因为如果值已经存在,usedInCol
、usedInRow
和 usedInBlock
将 return 1。所以 precheck
应该只在填满棋盘时使用,而不是之后。如果您检查从已经填满的板上获取的值,它将始终 return 1。
这是我第一次 post 在 Whosebug 上,所以如果我做错了什么,我深表歉意。我是 C 语言的新手,所以我确信我的代码在某些地方相当丑陋和骇人听闻,但是大部分代码都符合我的预期。我在使用 precheck
方法检查数独板之前遇到了问题,然后才开始通过我的求解器逻辑输入它。我正在重定向来自文本文件的输入,其中的字符串看起来像
4.....8.5.34.........7......2.....6.....8.4......1.......6.3.7.5..2.....1.4...... 4.....8.5.3..........7......2.....6.....8.4......1.......6.3.7.5..2.....1.5...... 4.....8.5.3..........7......2.....6.....8.4......1...x...6.3.7.5..2.....1.4...... 417369825632158947958724316825437169791586432346912758289643571573291684164875293 417369825632158947958724316825437169791586432346912758289643.71573291684164875293
每个字符串(理想情况下)为 81 个字符,仅包含数字 1-9 和“.”。我把一个字符串解析成一个temp char数组,然后使用方法fillBoard
将temp数组中的chars转成一个2d int数组。完成后,我调用 precheck
方法。如果填满的棋盘没有通过行、列和框检查,precheck
方法 returns 一个,表示拼图不可解(意味着应该打印一条错误消息,并且程序应该移动到下一个字符串)。出于某种原因,我的 precheck
方法即使对于应该可解的字符串也返回一个。我不确定这是为什么。任何帮助,将不胜感激。谢谢。
#include <stdio.h>
#include <string.h>
int alphaError = 0;
struct Point findEmpty(int board[9][9]);
int usedInBox(int board[9][9], int boxStartRow, int boxStartCol, int num);
int positionSafe(int board[9][9], int row, int col, int num);
int usedInCol(int board[9][9], int col, int num);
int usedInRow(int board[9][9], int row, int num);
int solvePuzzle(int board[9][9]);
int precheck(int board[9][9]);
int main()
{
char c;
int charCount = 0;
int i = 0;
char tempStr[100000];
int board[9][9];
while((fscanf(stdin, "%c", &c)) != EOF)
{
printf("%c", c);
if(c != '\n')
{
if(isalpha(c))
{
alphaError = 1;
}
tempStr[i] = c;
i++;
charCount++;
}
else
{
if(charCount != 81 || alphaError == 1)
{
printf("Error\n\n");
i = 0;
charCount = 0;
alphaError = 0;
}
else
{
fillBoard(board, tempStr);
printBoard(board);
if(precheck(board) == 1)
{
printf("Error\n\n");
}
else
{
if(solvePuzzle(board) == 1)
{
printBoard(board);
}
else
{
printf("No solution\n\n");
}
}
i = 0;
charCount = 0;
}
}
}
return 0;
}
struct Point
{
int x;
int y;
} point;
struct Point findEmpty(int board[9][9])
{
struct Point point1;
point1.x = -1;
point1.y = -1;
int row, col;
for(row = 0; row < 9; row++)
{
for(col = 0; col < 9; col++)
{
if(board[row][col] == 0)
{
point1.x = col;
point1.y = row;
}
}
}
return point1;
}
int usedInBox(int board[9][9], int boxStartRow, int boxStartCol, int num)
{
int row, col;
for(row = 0; row < 3; row++)
{
for(col = 0; col < 3; col++)
{
if(board[row + boxStartRow][col + boxStartCol] == num)
{
return 1;
}
}
}
return 0;
}
int positionSafe(int board[9][9], int row, int col, int num)
{
if((usedInRow(board, row, num)) == 0 && (usedInCol(board, col, num)) == 0 && (usedInBox(board, (row-row%3), (col-col%3), num)) == 0)
{
return 1;
}
else
{
return 0;
}
}
int usedInCol(int board[9][9], int col, int num)
{
int row;
for(row = 0; row < 9; row++)
{
if(board[row][col] == num)
{
return 1;
}
}
return 0;
}
int usedInRow(int board[9][9], int row, int num)
{
int col;
for(col = 0; col < 9; col++)
{
if(board[row][col] == num)
{
return 1;
}
}
return 0;
}
int solvePuzzle(int board[9][9])
{
int num;
struct Point point2;
point2 = findEmpty(board);
if(point2.x == -1)
{
return 1;
}
for(num = 1; num <= 9; num++)
{
if(positionSafe(board, point2.y, point2.x, num) == 1)
{
board[point2.y][point2.x] = num;
if(solvePuzzle(board) == 1)
{
return 1;
}
board[point2.y][point2.x] = 0;
}
}
return 0;
}
void printBoard(int board[9][9])
{
int i, j;
for (i = 0; i < 9; i++)
{
for (j = 0; j < 9; j++)
{
printf("%d", board[i][j]);
}
}
printf("\n\n");
}
void fillBoard(int board[9][9], char tempStr[100000])
{
int i, j;
int k = 0;
for(i = 0; i < 9; i++)
{
for(j = 0; j < 9; j++)
{
if(tempStr[k] == '.')
{
board[i][j] = 0;
}
else
{
board[i][j] = (tempStr[k] - '0');
}
k++;
}
}
}
int precheck(int board[9][9])
{
int i, j, num;
for(i = 0; i < 9; i++)
{
for(j = 0; j < 9; j++)
{
if(board[i][j] != 0)
{
num = board[i][j];
if(positionSafe(board, i, j, num) == 0)
{
return 1;
}
}
}
}
return 0;
}
所以您在已经填满的板上使用 precheck
?这可能是问题所在,因为如果值已经存在,usedInCol
、usedInRow
和 usedInBlock
将 return 1。所以 precheck
应该只在填满棋盘时使用,而不是之后。如果您检查从已经填满的板上获取的值,它将始终 return 1。