/* * qqwing - Sudoku solver and generator * Copyright (C) 2006-2014 Stephen Ostermiller http://ostermiller.org/ * Copyright (C) 2007 Jacques Bensimon (jacques@ipm.com) * Copyright (C) 2011 Jean Guillerez (j.guillerez - orange.fr) * Copyright (C) 2014 Michael Catanzaro (mcatanzaro@gnome.org) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "config.h" #include #include #include #include #if HAVE_GETTIMEOFDAY == 1 #include #else #include #endif #include "qqwing.hpp" using namespace qqwing; using namespace std; long getMicroseconds(); bool readPuzzleFromStdIn(int* puzzle); void printHelp(); void printVersion(); void printAbout(); /** * Main method -- the entry point into the program. * Run with --help as an argument for usage and documentation */ int main(int argc, char *argv[]){ try { // Start time for the application for timing long applicationStartTime = getMicroseconds(); // The number of puzzles solved or generated. int puzzleCount = 0; enum Action {NONE, GENERATE, SOLVE}; // defaults for options bool printPuzzle = false; bool printSolution = false; bool printHistory = false; bool printInstructions = false; bool timer = false; bool countSolutions = false; Action action = NONE; bool logHistory = false; SudokuBoard::PrintStyle printStyle = SudokuBoard::READABLE; int numberToGenerate = 1; bool printStats = false; SudokuBoard::Difficulty difficulty = SudokuBoard::UNKNOWN; SudokuBoard::Symmetry symmetry = SudokuBoard::NONE; // Read the arguments and set the options {for (int i=1; isetRecordHistory(printHistory || printInstructions || printStats || difficulty!=SudokuBoard::UNKNOWN); ss->setLogHistory(logHistory); ss->setPrintStyle(printStyle); // Solve puzzle or generate puzzles // until end of input for solving, or // until we have generated the specified number. bool done = false; int numberGenerated = 0; while (!done){ // record the start time for the timer. long puzzleStartTime = getMicroseconds(); // iff something has been printed for this particular puzzle bool printedSomething = false; // Record whether the puzzle was possible or not, // so that we don't try to solve impossible givens. bool havePuzzle = false; if (action == GENERATE){ // Generate a puzzle havePuzzle = ss->generatePuzzleSymmetry(symmetry); if (!havePuzzle && printPuzzle){ cout << "Could not generate puzzle."; if (printStyle==SudokuBoard::CSV){ cout << ","; } else { cout << endl; } printedSomething = true; } } else { // Read the next puzzle on STDIN int* puzzle = new int[BOARD_SIZE]; if (readPuzzleFromStdIn(puzzle)){ havePuzzle = ss->setPuzzle(puzzle); if (!havePuzzle){ if (printPuzzle){ ss->printPuzzle(); printedSomething = true; } if (printSolution) { cout << "Puzzle is not possible."; if (printStyle==SudokuBoard::CSV){ cout << ","; } else { cout << endl; } printedSomething = true; } } } else { // Set loop to terminate when nothing is left on STDIN havePuzzle = false; done = true; } delete[] puzzle; } int solutions = 0; if (havePuzzle){ // Count the solutions if requested. // (Must be done before solving, as it would // mess up the stats.) if (countSolutions){ solutions = ss->countSolutions(); } // Solve the puzzle if (printSolution || printHistory || printStats || printInstructions || difficulty!=SudokuBoard::UNKNOWN){ ss->solve(); } // Bail out if it didn't meet the difficulty standards for generation if (action == GENERATE){ if (difficulty!=SudokuBoard::UNKNOWN && difficulty!=ss->getDifficulty()){ havePuzzle = false; } else { numberGenerated++; // Set loop to terminate if enough have been generated. if (numberGenerated >= numberToGenerate) done = true; } } } // Check havePuzzle again, it may have changed based on difficulty if (havePuzzle){ // With a puzzle now in hand and possibly solved // print out the solution, stats, etc. printedSomething = true; // Record the end time for the timer. long puzzleDoneTime = getMicroseconds(); // Print the puzzle itself. if (printPuzzle) ss->printPuzzle(); // Print the solution if there is one if (printSolution){ if (ss->isSolved()){ ss->printSolution(); } else { cout << "Puzzle has no solution."; if (printStyle==SudokuBoard::CSV){ cout << ","; } else { cout << endl; } } } // Print the steps taken to solve or attempt to solve the puzzle. if (printHistory) ss->printSolveHistory(); // Print the instructions for solving the puzzle if (printInstructions) ss->printSolveInstructions(); // Print the number of solutions to the puzzle. if (countSolutions){ if (printStyle == SudokuBoard::CSV){ cout << solutions << ","; } else { if (solutions == 0){ cout << "There are no solutions to the puzzle." << endl; } else if (solutions == 1){ cout << "The solution to the puzzle is unique." << endl; } else { cout << "There are " << solutions << " solutions to the puzzle." << endl; } } } // Print out the time it took to solve the puzzle. if (timer){ double t = ((double)(puzzleDoneTime - puzzleStartTime))/1000.0; if (printStyle == SudokuBoard::CSV){ cout << t << ","; } else { cout << "Time: " << t << " milliseconds" << endl; } } // Print any stats we were able to gather while solving the puzzle. if (printStats){ int givenCount = ss->getGivenCount(); int singleCount = ss->getSingleCount(); int hiddenSingleCount = ss->getHiddenSingleCount(); int nakedPairCount = ss->getNakedPairCount(); int hiddenPairCount = ss->getHiddenPairCount(); int pointingPairTripleCount = ss->getPointingPairTripleCount(); int boxReductionCount = ss->getBoxLineReductionCount(); int guessCount = ss->getGuessCount(); int backtrackCount = ss->getBacktrackCount(); string difficultyString = ss->getDifficultyAsString(); if (printStyle == SudokuBoard::CSV){ cout << givenCount << "," << singleCount << "," << hiddenSingleCount << "," << nakedPairCount << "," << hiddenPairCount << "," << pointingPairTripleCount << "," << boxReductionCount << "," << guessCount << "," << backtrackCount << "," << difficultyString << ","; } else { cout << "Number of Givens: " << givenCount << endl; cout << "Number of Singles: " << singleCount << endl; cout << "Number of Hidden Singles: " << hiddenSingleCount << endl; cout << "Number of Naked Pairs: " << nakedPairCount << endl; cout << "Number of Hidden Pairs: " << hiddenPairCount << endl; cout << "Number of Pointing Pairs/Triples: " << pointingPairTripleCount << endl; cout << "Number of Box/Line Intersections: " << boxReductionCount << endl; cout << "Number of Guesses: " << guessCount << endl; cout << "Number of Backtracks: " << backtrackCount << endl; cout << "Difficulty: " << difficultyString << endl; } } puzzleCount++; } if (printedSomething && printStyle == SudokuBoard::CSV){ cout << endl; } } delete ss; long applicationDoneTime = getMicroseconds(); // Print out the time it took to do everything if (timer){ double t = ((double)(applicationDoneTime - applicationStartTime))/1000000.0; cout << puzzleCount << " puzzle" << ((puzzleCount==1)?"":"s") << " " << (action==GENERATE?"generated":"solved") << " in " << t << " seconds." << endl; } } catch (char const* s){ cout << s << endl; return 1; } return 0; } void printVersion(){ cout << PACKAGE_STRING << endl; } void printAbout(){ cout << "qqwing - Sudoku solver and generator" << endl; cout << "Copyright (C) 2006-2014 Stephen Ostermiller http://ostermiller.org/" << endl; cout << "Copyright (C) 2007 Jacques Bensimon (jacques@ipm.com)" << endl; cout << "Copyright (C) 2011 Jean Guillerez (j.guillerez - orange.fr)" << endl; cout << "Copyright (C) 2014 Michael Catanzaro (mcatanzaro@gnome.org)" << endl; cout << "" << endl; cout << "This program is free software; you can redistribute it and/or modify" << endl; cout << "it under the terms of the GNU General Public License as published by" << endl; cout << "the Free Software Foundation; either version 2 of the License, or" << endl; cout << "(at your option) any later version." << endl; cout << "" << endl; cout << "This program is distributed in the hope that it will be useful," << endl; cout << "but WITHOUT ANY WARRANTY; without even the implied warranty of" << endl; cout << "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the" << endl; cout << "GNU General Public License for more details." << endl; cout << "" << endl; cout << "You should have received a copy of the GNU General Public License along" << endl; cout << "with this program; if not, write to the Free Software Foundation, Inc.," << endl; cout << "51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA." << endl; } void printHelp(){ cout << "qqwing " << endl; cout << "Sudoku solver and generator." << endl; cout << " --generate Generate new puzzles" << endl; cout << " --solve Solve all the puzzles from standard input" << endl; cout << " --difficulty Generate only simple, easy, intermediate, expert, or any" << endl; cout << " --symmetry Symmetry: none, rotate90, rotate180, mirror, flip, or random" << endl; cout << " --puzzle Print the puzzle (default when generating)" << endl; cout << " --nopuzzle Do not print the puzzle (default when solving)" << endl; cout << " --solution Print the solution (default when solving)" << endl; cout << " --nosolution Do not print the solution (default when generating)" << endl; cout << " --stats Print statistics about moves used to solve the puzzle" << endl; cout << " --nostats Do not print statistics (default)" << endl; #if HAVE_GETTIMEOFDAY == 1 cout << " --timer Print time to generate or solve each puzzle" << endl; cout << " --notimer Do not print solve or generation times (default)" << endl; #endif cout << " --count-solutions Count the number of solutions to puzzles" << endl; cout << " --nocount-solutions Do not count the number of solutions (default)" << endl; cout << " --history Print trial and error used when solving" << endl; cout << " --nohistory Do not print trial and error to solve (default)" << endl; cout << " --instructions Print the steps (at least 81) needed to solve the puzzle" << endl; cout << " --noinstructions Do not print steps to solve (default)" << endl; cout << " --log-history Print trial and error to solve as it happens" << endl; cout << " --nolog-history Do not print trial and error to solve as it happens" << endl; cout << " --one-line Print puzzles on one line of 81 characters" << endl; cout << " --compact Print puzzles on 9 lines of 9 characters" << endl; cout << " --readable Print puzzles in human readable form (default)" << endl; cout << " --csv Output CSV format with one line puzzles" << endl; cout << " --help Print this message" << endl; cout << " --about Author and license information" << endl; cout << " --version Display current version number" << endl; } /** * Read a sudoku puzzle from standard input. * STDIN is processed one character at a time * until the sudoku is filled in. Any digit * or period is used to fill the sudoku, any * other character is ignored. */ bool readPuzzleFromStdIn(int* puzzle){ int read = 0; while (read < BOARD_SIZE){ char c = getchar(); if (c == EOF) return false; if (c >= '1' && c <='9'){ puzzle[read] = c-'0'; read++; } if (c == '.' || c == '0'){ puzzle[read] = 0; read++; } } return true; } /** * Get the current time in microseconds. */ long getMicroseconds(){ #if HAVE_GETTIMEOFDAY == 1 struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec*1000000+tv.tv_usec; #else return 0; #endif }