mightyscape-1.2/extensions/fablabchemnitz/sudoku/main.cpp

517 lines
18 KiB
C++

/*
* 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 <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#if HAVE_GETTIMEOFDAY == 1
#include <sys/time.h>
#else
#include <time.h>
#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; i<argc; i++){
if (!strcmp(argv[i],"--puzzle")){
printPuzzle = true;
} else if (!strcmp(argv[i],"--nopuzzle")){
printPuzzle = false;
} else if (!strcmp(argv[i],"--solution")){
printSolution = true;
} else if (!strcmp(argv[i],"--nosolution")){
printSolution = false;
} else if (!strcmp(argv[i],"--history")){
printHistory = true;
} else if (!strcmp(argv[i],"--nohistory")){
printHistory = false;
} else if (!strcmp(argv[i],"--instructions")){
printInstructions = true;
} else if (!strcmp(argv[i],"--noinstructions")){
printInstructions = false;
} else if (!strcmp(argv[i],"--stats")){
printStats = true;
} else if (!strcmp(argv[i],"--nostats")){
printStats = false;
#if HAVE_GETTIMEOFDAY == 1
} else if (!strcmp(argv[i],"--timer")){
timer = true;
} else if (!strcmp(argv[i],"--notimer")){
timer = false;
#endif
} else if (!strcmp(argv[i],"--count-solutions")){
countSolutions = true;
} else if (!strcmp(argv[i],"--nocount-solutions")){
countSolutions = false;
} else if (!strcmp(argv[i],"--generate")){
action = GENERATE;
printPuzzle = true;
if (i+1 < argc && argv[i+1][0] != '-'){
numberToGenerate = atoi(argv[i+1]);
if (numberToGenerate <= 0){
cout << "Bad number of puzzles to generate: " << argv[i+1] << endl;
return 1;
}
i++;
}
} else if (!strcmp(argv[i],"--difficulty")){
if (argc <= i+1){
cout << "Please specify a difficulty." << endl;
return 1;
} else if (!strcmp(argv[i+1],"simple")){
difficulty = SudokuBoard::SIMPLE;
} else if (!strcmp(argv[i+1],"easy")){
difficulty = SudokuBoard::EASY;
} else if (!strcmp(argv[i+1],"intermediate")){
difficulty = SudokuBoard::INTERMEDIATE;
} else if (!strcmp(argv[i+1],"expert")){
difficulty = SudokuBoard::EXPERT;
} else if (!strcmp(argv[i+1],"any")){
difficulty = SudokuBoard::UNKNOWN;
} else {
cout << "Difficulty expected to be simple, easy, intermediate, expert, or any, not " << argv[i+1] << endl;
return 1;
}
i++;
} else if (!strcmp(argv[i],"--symmetry")){
if (argc <= i+1){
cout << "Please specify a symmetry." << endl;
return 1;
} else if (!strcmp(argv[i+1],"none")){
symmetry = SudokuBoard::NONE;
} else if (!strcmp(argv[i+1],"rotate90")){
symmetry = SudokuBoard::ROTATE90;
} else if (!strcmp(argv[i+1],"rotate180")){
symmetry = SudokuBoard::ROTATE180;
} else if (!strcmp(argv[i+1],"mirror")){
symmetry = SudokuBoard::MIRROR;
} else if (!strcmp(argv[i+1],"flip")){
symmetry = SudokuBoard::FLIP;
} else if (!strcmp(argv[i+1],"random")){
symmetry = SudokuBoard::RANDOM;
} else {
cout << "Symmetry expected to be none, rotate90, rotate180, mirror, flip, or random, not " << argv[i+1] << endl;
return 1;
}
i++;
} else if (!strcmp(argv[i],"--solve")){
action = SOLVE;
printSolution = true;
} else if (!strcmp(argv[i],"--log-history")){
logHistory = true;
} else if (!strcmp(argv[i],"--nolog-history")){
logHistory = false;
} else if (!strcmp(argv[i],"--one-line")){
printStyle=SudokuBoard::ONE_LINE;
} else if (!strcmp(argv[i],"--compact")){
printStyle=SudokuBoard::COMPACT;
} else if (!strcmp(argv[i],"--readable")){
printStyle=SudokuBoard::READABLE;
} else if (!strcmp(argv[i],"--csv")){
printStyle=SudokuBoard::CSV;
} else if (!strcmp(argv[i],"-n") || !strcmp(argv[i],"--number")){
if (i+1 < argc){
numberToGenerate = atoi(argv[i+1]);
i++;
} else {
cout << "Please specify a number." << endl;
return 1;
}
} else if (!strcmp(argv[i],"-h") || !strcmp(argv[i],"--help") || !strcmp(argv[i],"help") || !strcmp(argv[i],"?")){
printHelp();
return 0;
} else if (!strcmp(argv[i],"--version")){
printVersion();
return 0;
} else if (!strcmp(argv[i],"--about")){
printAbout();
return 0;
} else {
cout << "Unknown argument: '" << argv[i] << "'" << endl;
printHelp();
return 1;
}
}}
if (action == NONE){
cout << "Either --solve or --generate must be specified." << endl;
printHelp();
return 1;
}
// Initialize the random number generator
srand ( unsigned ( time(0) ) );
// If printing out CSV, print a header
if (printStyle == SudokuBoard::CSV){
if (printPuzzle) cout << "Puzzle,";
if (printSolution) cout << "Solution,";
if (printHistory) cout << "Solve History,";
if (printInstructions) cout << "Solve Instructions,";
if (countSolutions) cout << "Solution Count,";
if (timer) cout << "Time (milliseconds),";
if (printStats) cout << "Givens,Singles,Hidden Singles,Naked Pairs,Hidden Pairs,Pointing Pairs/Triples,Box/Line Intersections,Guesses,Backtracks,Difficulty";
cout << "" << endl;
}
// Create a new puzzle board
// and set the options
SudokuBoard* ss = new SudokuBoard();
ss->setRecordHistory(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 <options>" << endl;
cout << "Sudoku solver and generator." << endl;
cout << " --generate <num> Generate new puzzles" << endl;
cout << " --solve Solve all the puzzles from standard input" << endl;
cout << " --difficulty <diff> Generate only simple, easy, intermediate, expert, or any" << endl;
cout << " --symmetry <sym> 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
}