// // pbnxpm.C - color nonogram solver // // 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. // // Copyright 2003 Alice J Corbin #include #include #include #include #include #include // Usage static std::string usage="Usage: pbmxpm [options] {xpm file} [xpm file]..\n\ Where possible options are:\n\ \n\ -p Don't display the preliminary solution\n\ -f Don't display the final solution(s)\n\ -c Don't display the count\n\ -t Don't display the title\n\ -e Do exhaustive preliminary processing\n\ -n number Stop after the specified number of solutions.\n"; // Command-line options static bool prelimOpt = true; static bool finalOpt = true; static bool countOpt = true; static bool titleOpt = true; static bool exhaustiveOpt = false; static int maxSolutionsOpt = MAXINT; // Size (in all dimensions) of the puzzle being solved static int numRows; static int numColumns; static int numColors; // The list of color characters static std::string colors; // Character that denotes white in the puzzle static char white; // How many solutions have we found for it? static int numSolutions; // Convenience typedef for a vector of strings typedef std::vector stringVector; // The keys for each row and column stringVector rowKeys; stringVector columnKeys; // Every possible permutation of squares for each row and column std::vector possibleRows; std::vector possibleColumns; // Number at which we balk at performing the fits static const unsigned int TooBig = 5000; // 500 is OK, 8000 is a bit much // Wildcard used to stand for 0 to many white squares static const std::string hole(1,'*'); // Internal class to approximate a doubly-indexed array // Each cell contains a single color. class Image { std::vector data; int nRows, nCols; static const char unknown = '?'; public: Image(int r, int c) { nRows = r; nCols = c; data.resize(nRows); }; //~Image(); inline void setRow(int r, std::string row) { data[r] = row; }; inline std::string getRow(int r) { return data[r]; }; inline std::string getColumn(int c) { std::string column = ""; for (int r=0; r > data; int nRows, nCols; static const char unknown = '?'; public: BImage(int r, int c) { nRows = r; nCols = c; data.resize(nRows); for (int r=0; r=0; i--) { std::string line(key); std::string whiteSpace(i, white); line.replace(line.find_first_of(hole), 1, whiteSpace); possibleLines(length, line, possibles); } // } // catch(...) { std::cerr << "Ouch!" << std::endl;} } // trim - attempt to eliminate some of the possible rows and columns bool trim() { BImage image(numRows, numColumns); bool changed = false; // Or all of the possible rows together, to find some squares for (int r=0; r TooBig) && !exhaustiveOpt) { // For some reason that I don't understand, the fits functions // take substantially longer than the or functions, and for large // numbers of possible rows this adds up to an intolerable // amount of time. What I was going to do if the number was // too big to fit, was to throw away the old set of possibles // and rebuild it, checking the fit and throwing out possibles // recursively. But, by gum, not doing anything in this case // also works. } else { stringVector::iterator p = possibleRows[r].begin(); while (p != possibleRows[r].end()) { if (image.fitsRow(r, *p)) p++; else { possibleRows[r].erase(p); changed = true; } } } } for (int c=0; c TooBig) && !exhaustiveOpt) { //std::cout << "...skipping..." << std::flush; } else { stringVector::iterator p = possibleColumns[c].begin(); while (p != possibleColumns[c].end()) { if (image.fitsColumn(c, *p)) p++; else { possibleColumns[c].erase(p); changed = true; } } } } //image.print(); std::cout << std::endl; if (!changed && prelimOpt) { image.print(); } return changed; } // Make sure that some set of possible columns match the image so far. bool checkWeave(Image& image, int thisRow) { for (int c=0; c= maxSolutionsOpt) return; } } } // solve - find all possible solutions to the given keys. void solve() { // For each key, assemble a list of every possible row or column. possibleRows.clear(); for (int r=0; r0; i--) { if (key[i] != white && key[i-1] != white && key[i] != key[i-1]) { key.insert(i,hole); } } // The key must begin and end with a 0-many white block key.replace(0, key.find_first_not_of(white), hole); key.replace(key.find_last_not_of(white)+1, key.npos, hole); // All interior blocks of white squares must be replaced... std::string::size_type w; while (key.npos != (w = key.find_first_of(white))) { std::string::size_type d = key.find_first_not_of(white,w); // ...by either a 'normal' 0 to many white block, if (key[w-1] != key[d]) key.replace(w, d-w, hole); // ...or by a 1 to many white block. else key.replace(w, d-w, specialCase); } // Now that we've gotten rid of all of the white squares, replace each // special-case 1 to many block with a white square and a 'normal' hole. while (key.npos != (w = key.find_first_of(specialCase))) { key.replace(w, 1, specialCase1); } return key; } // decodexpm: decode the picture in the xpm file // Figure out the numbers of rows, columns and colors, // and create the row and column keys that have to be matched. void decodexpm(char* filename) { char buffer[66]; char unused; int i, r, c; std::ifstream in (filename); if (!in) error("cannot open", filename); // Throw away the first two lines. in.getline(buffer, sizeof(buffer)); // /* XPM */ in.getline(buffer, sizeof(buffer)); // static char *abc[]={ // Read the numbers out of the next line. //"16 16 4 1", in >> unused >> numColumns >> numRows >> numColors >> buffer; // Read the color designators out of the next n lines // "b c #000000", colors.resize(numColors); white = ' '; for (i=0; i> unused >> colors[i] >> unused >> buffer; if (0 == strcmp(buffer,"#ffffff\",")) white = colors[i]; } // Read the image into memory // "#..#..aaa.......", Image image(numRows, numColumns); for (r=0, i=0; r> unused >> row >> buffer; image.setRow(r, row); } // Create the row and column keys for this image rowKeys.resize(numRows); for (r=0; r