/* file: sudoku.c * author: rick van der Zwet 0433373, 2006 * University Leiden, LIACS */ #include #include #include #include #include #define MAX_XYZ 9 #define EMPTY_SPOT '_' #define bold "[1m" #define normal "[0m" #define DEBUGPLACE stderr const char esc = 27; /* escape character */ int debug(const int level = 0) { #if DEBUG if (level <= DEBUG) { return(1); } else { return(0); } #else return(0); #endif } class Sudoku { public: Sudoku(); void printNice(const int boldRow = -1, const int boldCol = -1, const char emptcolSpace = EMPTY_SPOT); void printRaw(); void printPossible(); void printPossibleAtNumber(const int row, const int col); void reset(); int randomNumbers(const double chance); int fromFile(FILE *handle); int insert(const int row, const int col, const int number); int solutionFinder(); private: int _sudoku[MAX_XYZ][MAX_XYZ]; bool _possible[MAX_XYZ][MAX_XYZ][MAX_XYZ]; int _numbersFilled; void _updatePossibleList(const int row, const int col, const int number); bool _solution1(int &row, int &col, int &number); bool _solution2(int &row, int &col, int &number); bool _solution3a(int &row, int &col, int &number); bool _solution3b(int &row, int &col, int &number); }; Sudoku::Sudoku() { reset(); } void Sudoku::printRaw() { int row,col; for (row = 0; row < MAX_XYZ ; row++) { for (col = 0; col < MAX_XYZ; col++) { printf("%i ", _sudoku[row][col]); } printf("\n"); } } void Sudoku::printNice(const int boldRow, const int boldCol, const char emptcolSpace) { int row,col; int tmpRow; const int maxRow = (MAX_XYZ + (MAX_XYZ / 3)) * 2 + 1; for(tmpRow = 0; tmpRow < maxRow; tmpRow++) { if ((tmpRow%8) == 0) { printf("+"); }else {printf("-");} } printf("\n"); for (row = 0; row < MAX_XYZ ; row++) { printf("| "); for (col = 0; col < MAX_XYZ; col++) { if ( _sudoku[row][col] == 0) { printf("%c ",emptcolSpace); } else { if (row == boldRow && col == boldCol) { printf("%c%s%i%c%s ",esc, bold, _sudoku[row][col], esc, normal); } else { printf("%i ", _sudoku[row][col]); } } if ( ((col - 2) % 3) == 0 ) { printf("| "); } } printf("\n"); if ( ((row - 2) % 3) == 0 ) { for(tmpRow = 0; tmpRow < maxRow; tmpRow++) { if ((tmpRow%8) == 0) { printf("+"); } else { printf("-"); } } printf("\n"); } } printf("\n\n"); } void Sudoku::printPossibleAtNumber(const int row, const int col) { if (debug(3)) { int tmpPossible; fprintf(DEBUGPLACE, "Possible at [%i,%i]: ", row, col); for (tmpPossible = 0; tmpPossible < MAX_XYZ; tmpPossible++) { if (_possible[row][col][tmpPossible] == true) { fprintf(DEBUGPLACE,"%i ", tmpPossible + 1); } } fprintf(DEBUGPLACE, "\n"); } } void Sudoku::printPossible() { int row,col; if (debug(3)) { for (row = 0; row < MAX_XYZ ; row++) { for (col = 0; col < MAX_XYZ; col++) { if(_sudoku[row][col] == 0) { printPossibleAtNumber(row,col); } } } } } void Sudoku::_updatePossibleList(const int row, const int col, const int number) { int tmpRow; int tmpCol; int blockRow; int blockCol; int c; /* Row and Col and Number own */ for (c = 0; c < MAX_XYZ; c++) { _possible[row][col][c] = false; _possible[row][c][number - 1] = false; _possible[c][col][number - 1] = false; } /* Block */ tmpRow = row - (row % (MAX_XYZ / 3)); tmpCol = col - (col % (MAX_XYZ / 3)); for (blockRow = tmpRow; blockRow < (tmpRow + 3); blockRow++) { for (blockCol = tmpCol; blockCol < (tmpCol + 3); blockCol++) { _possible[blockRow][blockCol][number - 1] = false; } } } int Sudoku::insert(const int row, const int col, const int number) { if (debug(1)) { fprintf(DEBUGPLACE, "Will insert %i at [%i,%i]: %i\n", number, row, col, _possible[row][col][number - 1]); } if (_possible[row][col][number - 1] == true) { _updatePossibleList(row, col, number); _sudoku[row][col] = number; _numbersFilled++; return(0); } else { return(1); } } int Sudoku::randomNumbers(const double chance) { if (debug(1)) { fprintf(DEBUGPLACE, "Running createsudoku\n"); } int row; int col; int c; int randomnumber; int numberdone[MAX_XYZ]; int numbertried; srand( (unsigned)time( NULL ) ); for (row = 0; row < MAX_XYZ; row++) { for (col = 0; col < MAX_XYZ; col++) { if (rand() < (RAND_MAX * (chance / 100))) { numbertried = 0; for (c = 0; c < MAX_XYZ; c++ ) { numberdone[c] = 0; } do { if (numbertried == 9) { return(1); } else { numbertried++; } do { randomnumber = 1 + (int) ( (double)MAX_XYZ * (random() / (RAND_MAX + 1.0))); } while (numberdone[randomnumber - 1] == 1); numberdone[randomnumber - 1] = 1; if (debug(2)) { fprintf(DEBUGPLACE, "Random check number %i at [%i,%i]\n", randomnumber, row,col); } } while ( insert(row, col, randomnumber) == 1); } else { _sudoku[row][col] = 0; } } } return(0); } void Sudoku::reset() { //temp counters int row; int col; int z; _numbersFilled = 0; for (row = 0; row < MAX_XYZ; row++) { for (col = 0; col < MAX_XYZ; col++) { _sudoku[row][col] = 0; for (z = 0; z < MAX_XYZ; z++) { _possible[row][col][z] = true; } } } } int Sudoku::fromFile(FILE *handle) { int row; int col; int tmpInt; for (row = 0; row < MAX_XYZ; row++) { for (col = 0; col < MAX_XYZ; col++) { fscanf(handle, "%1i", &tmpInt); if (tmpInt != 0) { insert(row, col, tmpInt); } } } fclose(handle); return(0); } /* Use Basic technique of finding places with just one number left to place */ bool Sudoku::_solution1(int &row, int &col, int &number) { #define NOTHING_FOUND 0 #define ONE_FOUND 1 #define MORE_THEN_ONE_FOUND 2 int tmpRow; int tmpCol; int tmpPossible; int tmpNumber; int searchState; for (tmpRow = 0; tmpRow < MAX_XYZ; tmpRow++) { for (tmpCol = 0; tmpCol < MAX_XYZ; tmpCol++) { if (_sudoku[tmpRow][tmpCol] == 0) { tmpNumber = 0; searchState = NOTHING_FOUND; for (tmpPossible = 0; tmpPossible < MAX_XYZ; tmpPossible++) { if (_possible[tmpRow][tmpCol][tmpPossible] == true && searchState == ONE_FOUND) { searchState = MORE_THEN_ONE_FOUND; break; } else if (_possible[tmpRow][tmpCol][tmpPossible] == true && searchState == NOTHING_FOUND) { tmpNumber = tmpPossible + 1; searchState = ONE_FOUND; } } if (searchState == ONE_FOUND) { row = tmpRow; col = tmpCol; number = tmpNumber; return(true); } } } } return(false); } /* Check if number can onlcol be placed somewhere, if not able to place anywhere else */ /* FIXME: Should be more modular, to reduce line length and readabilitcol */ /* FIXME: find nice solution for goto's */ bool Sudoku::_solution2(int &row, int &col, int &number) { int tmpRow; int tmpCol; int tmpPossible; int tmpNumber; int blockRow; int blockTmpRow; int blockCol; int blockTmpCol; bool result; for (tmpRow = 0; tmpRow < MAX_XYZ; tmpRow++) { for (tmpCol = 0; tmpCol < MAX_XYZ; tmpCol++) { if (_sudoku[tmpRow][tmpCol] == 0) { for (tmpPossible = 0; tmpPossible < MAX_XYZ; tmpPossible++) { if (_possible[tmpRow][tmpCol][tmpPossible] == true) { if (debug(3)) { fprintf(DEBUGPLACE, "DEBUG SOL 2: Checking [%i,%i], number: %i...\n", tmpRow,tmpCol,(tmpPossible +1)); } /* checkRow */ result = true; for (tmpNumber = 0; tmpNumber < MAX_XYZ; tmpNumber++) { if (_possible[tmpNumber][tmpCol][tmpPossible] == true && tmpRow != tmpNumber) { if (debug(3)) { fprintf(DEBUGPLACE, "DEBUG SOL 2: ..failed at Rowcheck bcol [%i,%i]\n", tmpNumber,tmpCol); } result = false; break; } } if (result == true) { if (debug(3)) { fprintf(DEBUGPLACE,"DEBUG SOL 2: ..succesfull at Rowcheck\n"); } goto solutionFound; } /* checkCol */ result = true; for (tmpNumber = 0; tmpNumber < MAX_XYZ; tmpNumber++) { if (_possible[tmpRow][tmpNumber][tmpPossible] == true && tmpCol != tmpNumber) { if (debug(3)) { fprintf( DEBUGPLACE, "DEBUG SOL 2: ..failed at Cowcheck bcol [%i,%i]\n", tmpRow,tmpNumber); } result = false; break; } } if (result == true) { if (debug(3)) { fprintf(DEBUGPLACE,"DEBUG SOL 2: ..succesfull at Cowcheck\n"); } goto solutionFound; } /* checkBlock */ result = true; blockTmpRow = tmpRow - (tmpRow % (MAX_XYZ / 3)); blockTmpCol = tmpCol - (tmpCol % (MAX_XYZ / 3)); for (blockRow = blockTmpRow; blockRow < (blockTmpRow + 3); blockRow++) { for (blockCol = blockTmpCol; blockCol < (blockTmpCol + 3); blockCol++) { if (debug(4)) { fprintf(DEBUGPLACE,"DEBUG SOL 2: ..checking [%i,%i]\n",blockRow,blockCol); } if(_possible[blockRow][blockCol][tmpPossible] == true && (not (tmpRow == blockRow && tmpCol == blockCol)) ) { if (debug(4)) { fprintf(DEBUGPLACE,"DEBUG SOL 2: ...will cause failure\n"); } result = false; goto blockCheckFailed; } else { if (debug(4)) { fprintf(DEBUGPLACE,"DEBUG SOL 2: ...will be no harm\n"); } } } } blockCheckFailed: if (result == true) { if (debug(3)) { fprintf(DEBUGPLACE,"DEBUG SOL 2: ..succesfull at Blockcheck\n"); } goto solutionFound; } } } } } } return(false); solutionFound: row = tmpRow; col = tmpCol; number = tmpPossible + 1; return(true); } /* available striper, row based */ /* FIXME: Should be more modular, to reduce line length and readabilitcol */ bool Sudoku::_solution3a(int &row, int &col, int &number) { int tmpRow; int tmpCol; int tmpPossible; int tmpPossible2; int tmpColNumber; int tmpNumber[MAX_XYZ]; int tmpAllNumbers; int tmpAllNumbersFound; bool result; for (tmpRow = 0; tmpRow < MAX_XYZ; tmpRow++) { for (tmpCol = 0; tmpCol < MAX_XYZ; tmpCol++) { if (_sudoku[tmpRow][tmpCol] == 0) { if (debug(3)) { fprintf(DEBUGPLACE,"SOL 3a DEBUG Checking [%i,%i]...\n",tmpRow,tmpCol); } /* Set all numbers */ tmpAllNumbers = 0; for (tmpPossible = 0; tmpPossible < MAX_XYZ; tmpPossible++) { if (_possible[tmpRow][tmpCol][tmpPossible] == true) { tmpNumber[tmpPossible] = 1; tmpAllNumbers++; } else { tmpNumber[tmpPossible] = 0; } } if (debug(3)) { fprintf(DEBUGPLACE,"SOL 3a DEBUG %i\n", tmpAllNumbers); } if (tmpAllNumbers != 0) { for (tmpColNumber = 0; tmpColNumber < MAX_XYZ; tmpColNumber++) { if (_sudoku[tmpRow][tmpColNumber] == 0 && tmpColNumber != tmpCol) { result = true; for (tmpPossible2 = 0; tmpPossible2 < MAX_XYZ; tmpPossible2++) { if ( _possible[tmpRow][tmpColNumber][tmpPossible2] == true && _possible[tmpRow][tmpCol][tmpPossible2] == false) { if (debug(3)) { fprintf(DEBUGPLACE,"SOL 3a DEBUG [%i,%i] failed at %i\n", tmpRow, tmpColNumber,tmpPossible2+1); } result = false; break; } } if (result == true) { if (debug(3)) { fprintf(DEBUGPLACE,"SOL 3a DEBUG [%i,%i] will increase\n", tmpRow, tmpColNumber); } for (tmpPossible2 = 0; tmpPossible2 < MAX_XYZ; tmpPossible2++) { if (_possible[tmpRow][tmpColNumber][tmpPossible2] == true) { if (debug(3)) { fprintf(DEBUGPLACE, "SOL 3a DEBUG [%i,%i] will increase possible %i to %i\n", tmpRow, tmpColNumber, tmpPossible2 + 1, tmpNumber[tmpPossible2] + 1); } if (tmpNumber[tmpPossible2] == tmpAllNumbers) { goto colCheckFailed; } else { tmpNumber[tmpPossible2]++; } } } } } } /* calculate if result found */ tmpAllNumbersFound = 1; for (tmpPossible = 0; tmpPossible < MAX_XYZ; tmpPossible++) { if (tmpNumber[tmpPossible] == tmpAllNumbers) { tmpAllNumbersFound++; if (debug(3)) { fprintf(DEBUGPLACE, "SOL 3b DEBUG %i reduced tmpAllNumbers to %i\n", tmpPossible + 1, tmpAllNumbers); } } } if (tmpAllNumbersFound == tmpAllNumbers) { for (tmpPossible = 0; tmpPossible < MAX_XYZ; tmpPossible++) { if (tmpNumber[tmpPossible] == 1) { row = tmpRow; col = tmpCol; number = tmpPossible + 1; return(true); } } } } } //end if colCheckFailed: if (debug(3)) { fprintf(DEBUGPLACE,"SOL 3c DEBUG no failed solution found\n"); } } } return(false); } /* available striper, col based, note row number will change ;-) */ /* FIXME: Should be more modular, to reduce line length and readabilitcol */ bool Sudoku::_solution3b(int &row, int &col, int &number) { int tmpRow; int tmpCol; int tmpPossible; int tmpPossible2; int tmpRowNumber; int tmpNumber[MAX_XYZ]; int tmpAllNumbers; int tmpAllNumbersFound; bool result; for (tmpRow = 0; tmpRow < MAX_XYZ; tmpRow++) { for (tmpCol = 0; tmpCol < MAX_XYZ; tmpCol++) { if (_sudoku[tmpRow][tmpCol] == 0) { if (debug(3)) { fprintf(DEBUGPLACE,"SOL 3b DEBUG Checking [%i,%i]...\n",tmpRow,tmpCol); } /* Set all numbers */ tmpAllNumbers = 0; for (tmpPossible = 0; tmpPossible < MAX_XYZ; tmpPossible++) { if (_possible[tmpRow][tmpCol][tmpPossible] == true) { tmpNumber[tmpPossible] = 1; tmpAllNumbers++; } else { tmpNumber[tmpPossible] = 0; } } if (debug(3)) { fprintf(DEBUGPLACE,"SOL 3b DEBUG %i\n", tmpAllNumbers); } if (tmpAllNumbers != 0) { for (tmpRowNumber = 0; tmpRowNumber < MAX_XYZ; tmpRowNumber++) { if (_sudoku[tmpRowNumber][tmpCol] == 0 && tmpRowNumber != tmpRow) { result = true; for (tmpPossible2 = 0; tmpPossible2 < MAX_XYZ; tmpPossible2++) { if ( _possible[tmpRowNumber][tmpCol][tmpPossible2] == true && _possible[tmpRow][tmpCol][tmpPossible2] == false) { if (debug(3)) { fprintf(DEBUGPLACE, "SOL 3b DEBUG [%i,%i] failed at %i\n", tmpRowNumber, tmpCol,tmpPossible2+1); } result = false; break; } } if (result == true) { if (debug(3)) { fprintf(DEBUGPLACE, "SOL 3b DEBUG [%i,%i] will increase\n", tmpRowNumber, tmpCol); } for (tmpPossible2 = 0; tmpPossible2 < MAX_XYZ; tmpPossible2++) { if (_possible[tmpRowNumber][tmpCol][tmpPossible2] == true) { if (debug(3)) { fprintf(DEBUGPLACE, "SOL 3b DEBUG [%i,%i] will increase possible %i to %i\n", tmpRowNumber, tmpCol, tmpPossible2 + 1, tmpNumber[tmpPossible2] + 1); } if (tmpNumber[tmpPossible2] == tmpAllNumbers) { goto rowCheckFailed; } else { tmpNumber[tmpPossible2]++; } } } } } } /* calculate if result found */ tmpAllNumbersFound = 1; for (tmpPossible = 0; tmpPossible < MAX_XYZ; tmpPossible++) { if (tmpNumber[tmpPossible] == tmpAllNumbers) { tmpAllNumbersFound++; if (debug(3)) { fprintf(DEBUGPLACE, "SOL 3b DEBUG %i reduced tmpAllNumbers to %i\n", tmpPossible + 1, tmpAllNumbers); } } } if (tmpAllNumbersFound == tmpAllNumbers) { for (tmpPossible = 0; tmpPossible < MAX_XYZ; tmpPossible++) { if (tmpNumber[tmpPossible] == 1) { row = tmpRow; col = tmpCol; number = tmpPossible + 1; return(true); } } } } } //end if rowCheckFailed: if (debug(3)) { fprintf(DEBUGPLACE,"SOL 3b DEBUG no failed solution found\n"); } } } return(false); } int Sudoku::solutionFinder() { #define NO_SOLUTION 0 #define SOLUTION_1 1 #define SOLUTION_2 2 #define SOLUTION_3a 3 #define SOLUTION_3b 4 #define SOLUTION_3c 5 int row; int col; int number; bool gameAnalcolzed; int returnCode; int solutionNumber; gameAnalcolzed = false; while( gameAnalcolzed == false ) { solutionNumber = NO_SOLUTION; if (_numbersFilled == MAX_XYZ * MAX_XYZ) { gameAnalcolzed = true; returnCode = 0; } else { if (_solution1(row, col, number) == true) { solutionNumber = SOLUTION_1; } else if(_solution2(row, col, number) == true) { solutionNumber = SOLUTION_2; } else if(_solution3a(row, col, number) == true) { solutionNumber = SOLUTION_3a; } else if(_solution3b(row, col, number) == true) { solutionNumber = SOLUTION_3b; } else { gameAnalcolzed = true; returnCode = 1; } } if (solutionNumber != NO_SOLUTION) { insert(row, col, number); printf("Filled in %i at [%i,%i] found with finder %i\n", number, row, col, solutionNumber); printf("Total Numbers filled %i\n", _numbersFilled); printNice(row, col); getchar(); } } return(returnCode); } int main(int argc, char *argv[]) { Sudoku puzzel; int timesFailed; int returnCode; returnCode = 0; if (argc == 3 && strcmp(argv[1], "-f") == 0) { if (strcmp(argv[2], "-") == 0) { if (debug(1)) { fprintf(DEBUGPLACE, "Reading from 'stdin'\n"); } puzzel.fromFile(stdin); } else { if (debug(1)) { fprintf(DEBUGPLACE, "Reading from %s\n",argv[2]); } puzzel.fromFile(fopen(argv[2], "r")); } printf("I have read this puzzle\n"); puzzel.printNice(); returnCode = puzzel.solutionFinder(); if (returnCode == 0) { printf("I Guess this will be the final solution\n"); puzzel.printNice(); } else { printf("Finding Solution failed, error code: %i\n", returnCode); printf("Failed at puzzle\n"); puzzel.printNice(); puzzel.printPossible(); } } else if (argc == 3 && strcmp(argv[1], "-c") == 0) { fprintf(DEBUGPLACE,"Please wait will generating puzzle"); timesFailed = 0; while (puzzel.randomNumbers(strtod(argv[2], NULL)) == 1 ) { timesFailed++; if (debug(1)) { fprintf(DEBUGPLACE, "Failed to created the puzzel at the %i round\n", timesFailed); } else { fprintf(DEBUGPLACE,"."); } puzzel.reset(); } fprintf(DEBUGPLACE, "\nResult, (NOTE(!): puzzle might not be solveble)\n"); puzzel.printRaw(); } else { printf("Usage %s -f \n",argv[0]); printf("NOTE 1: No error checking done, valid input puzzel will be "); printf("generated the -c flags\n"); printf("NOTE 2: if = -, stdin will be used as input"); printf("Usage %s -c To create a new puzzle\n",argv[0]); printf("NOTE 1: Above 60%% it will be a bit hard to create "); printf("a puzzle\n"); printf("NOTE 2: Puzzle generated will not always be solveble\n"); returnCode = 128; } return(returnCode); }