mirror of
https://github.com/BlackLight/libCSP--.git
synced 2024-11-14 15:27:15 +01:00
293 lines
6.1 KiB
C++
293 lines
6.1 KiB
C++
/*
|
|
* =====================================================================================
|
|
*
|
|
* Filename: sudoku.cpp
|
|
*
|
|
* Description: A small program that using libCSP++ allows you to solve a sudoku.
|
|
* The sudoku is passed as text file, an example of the format can
|
|
* be found in the file sudoku.txt. If no parameters is passed to the
|
|
* program, this default sudoku is loaded and solved, otherwise the
|
|
* sudoku chosen by the user in the specified text file is solved.
|
|
*
|
|
* Usage: ./sudoku <text file containing the sudoku>
|
|
* Complile: g++ -IPATH/TO/csp++.h -o sudoku sudoku.cpp
|
|
* Version: 1.0
|
|
* Created: 17/05/2010 09:22:25
|
|
* Revision: none
|
|
* Compiler: gcc
|
|
*
|
|
* Author: BlackLight (http://0x00.ath.cx), <blacklight@autistici.org>
|
|
* Licence: GNU GPL v.3
|
|
* Company: lulz
|
|
*
|
|
* =====================================================================================
|
|
*/
|
|
|
|
#include <iostream>
|
|
#include <fstream>
|
|
#include <string>
|
|
#include <cstdlib>
|
|
#include <cmath>
|
|
#include <csp++/csp++.h>
|
|
|
|
#define NOVALUE -1
|
|
#define DEFAULT_SUDOKU_FILE "sudoku.txt"
|
|
|
|
using namespace std;
|
|
|
|
int WIDTH = 0;
|
|
int HEIGHT = 0;
|
|
|
|
int
|
|
toID (int i, int j)
|
|
{
|
|
return (i * (WIDTH*WIDTH)) + j;
|
|
}
|
|
|
|
bool
|
|
rowsConstraint ( std::vector< CSPvariable<int> > variables)
|
|
{
|
|
for ( int i=0; i < WIDTH*WIDTH; i++ ) {
|
|
for ( int j=0; j < HEIGHT*HEIGHT; j++ ) {
|
|
for ( int k=0; k < j; k++ ) {
|
|
int var1 = toID(i,j);
|
|
int var2 = toID(i,k);
|
|
|
|
if ( variables[var1].fixed || variables[var2].fixed ) {
|
|
if ( variables[var1].value == variables[var2].value ) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
colsConstraint ( std::vector< CSPvariable<int> > variables)
|
|
{
|
|
for ( int i=0; i < HEIGHT*HEIGHT; i++ ) {
|
|
for ( int j=0; j < WIDTH*WIDTH; j++ ) {
|
|
for ( int k=0; k < j; k++ ) {
|
|
int var1 = toID(j,i);
|
|
int var2 = toID(k,i);
|
|
|
|
if ( variables[var1].fixed || variables[var2].fixed ) {
|
|
if ( variables[var1].value == variables[var2].value ) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
cellsConstraint ( std::vector< CSPvariable<int> > variables)
|
|
{
|
|
for ( int x=0; x < WIDTH*WIDTH; x += WIDTH ) {
|
|
for ( int y=0; y < HEIGHT*HEIGHT; y += HEIGHT ) {
|
|
for ( int i=x; i < x+WIDTH; i++ ) {
|
|
for ( int j=y; j < y+HEIGHT; j++ ) {
|
|
int var1 = toID(i,j);
|
|
|
|
for ( int k=x; k < i; k++ ) {
|
|
for ( int l=y; l < j; l++ ) {
|
|
int var2 = toID(k,l);
|
|
|
|
if ( variables[var1].fixed || variables[var2].fixed ) {
|
|
if ( variables[var1].value == variables[var2].value ) {
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void
|
|
printSudoku (CSP<int> csp)
|
|
{
|
|
for ( int i=0; i < WIDTH*WIDTH; i++ ) {
|
|
if (!(i % WIDTH))
|
|
cout << endl;
|
|
|
|
for ( int j=0; j < HEIGHT*HEIGHT; j++ ) {
|
|
int id = toID(j,i);
|
|
|
|
if (!(j % HEIGHT))
|
|
cout << " ";
|
|
|
|
if (csp.isSet(id))
|
|
cout << csp.value(id) << " ";
|
|
else
|
|
cout << ". ";
|
|
}
|
|
|
|
cout << endl;
|
|
}
|
|
|
|
cout << endl;
|
|
}
|
|
|
|
CSP<int>
|
|
getSudoku (const char* sudokuFile)
|
|
{
|
|
string line;
|
|
vector< vector<int> > sudoku;
|
|
ifstream in(sudokuFile);
|
|
|
|
if (!in)
|
|
throw "Unable to read input file";
|
|
|
|
for ( HEIGHT = 0; getline(in, line); HEIGHT++ ) {
|
|
vector<int> values;
|
|
string value = "";
|
|
bool hasNumbers = false;
|
|
|
|
for ( int j=0; j < line.length(); j++) {
|
|
if (line[j] >= '0' && line[j] <= '9') {
|
|
value += line[j];
|
|
hasNumbers = true;
|
|
} else {
|
|
if (value.length() > 0) {
|
|
values.push_back( atoi(value.c_str()) );
|
|
value = "";
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!hasNumbers) {
|
|
HEIGHT--;
|
|
continue;
|
|
}
|
|
|
|
if (value.length() > 0) {
|
|
values.push_back( atoi(value.c_str()) );
|
|
value = "";
|
|
}
|
|
|
|
if ( HEIGHT == 0 )
|
|
WIDTH = values.size();
|
|
else {
|
|
if (values.size() != WIDTH) {
|
|
in.close();
|
|
throw "The columns have different number of elements";
|
|
}
|
|
}
|
|
|
|
sudoku.push_back(values);
|
|
}
|
|
|
|
in.close();
|
|
|
|
if (HEIGHT != WIDTH)
|
|
throw "The grid is not a square";
|
|
|
|
WIDTH = (int) sqrt(WIDTH);
|
|
HEIGHT = (int) sqrt(HEIGHT);
|
|
vector<int> domain;
|
|
|
|
for ( int i=1; i <= WIDTH*WIDTH; i++)
|
|
domain.push_back(i);
|
|
|
|
CSP<int> csp( WIDTH*WIDTH * HEIGHT*HEIGHT, NOVALUE, false);
|
|
csp.appendConstraint(rowsConstraint);
|
|
csp.appendConstraint(colsConstraint);
|
|
csp.appendConstraint(cellsConstraint);
|
|
|
|
for ( int i=0; i < csp.getSize(); i++ )
|
|
csp.setDomain(i, domain);
|
|
|
|
for ( int i=0; i < sudoku.size(); i++ ) {
|
|
for ( int j=0; j < sudoku[i].size(); j++ ) {
|
|
if (sudoku[i][j] != 0) {
|
|
csp.setValue( toID(j,i), sudoku[i][j] );
|
|
}
|
|
}
|
|
}
|
|
|
|
return csp;
|
|
}
|
|
|
|
int
|
|
main ( int argc, char *argv[] )
|
|
{
|
|
string sudokuFile;
|
|
CSP<int> csp;
|
|
|
|
if (argc == 1) {
|
|
cout << "Loading default sudoku from " << DEFAULT_SUDOKU_FILE
|
|
<< ", pass a different file as argument if you want to "
|
|
<< "load a different sudoku\n\n";
|
|
sudokuFile = DEFAULT_SUDOKU_FILE;
|
|
} else {
|
|
sudokuFile = argv[1];
|
|
}
|
|
|
|
try {
|
|
csp = getSudoku(sudokuFile.c_str());
|
|
cout << "Sudoku to be solved:\n";
|
|
printSudoku(csp);
|
|
}
|
|
|
|
catch (const char* msg) {
|
|
cerr << "Exception: " << msg << endl;
|
|
return EXIT_FAILURE;
|
|
}
|
|
|
|
bool changed = false;
|
|
int steps = 1;
|
|
vector< vector<int> > oldDomains(csp.getSize());
|
|
|
|
do {
|
|
cout << "Solving ... step " << steps++ << endl;
|
|
|
|
for ( int i=0; i < csp.getSize(); i++ ) {
|
|
oldDomains[i] = csp.getDomain(i);
|
|
}
|
|
|
|
csp.refreshDomains();
|
|
csp.assignUniqueDomains();
|
|
|
|
if (csp.hasUniqueSolution())
|
|
break;
|
|
|
|
changed = false;
|
|
|
|
for ( int i=0; i < csp.getSize(); i++ ) {
|
|
if (csp.getDomain(i).size() != oldDomains[i].size())
|
|
changed = true;
|
|
|
|
for ( int j=0; j < csp.getDomain(i).size() && !changed; j++ ) {
|
|
if ( csp.getDomain(i)[j] != oldDomains[i][j] )
|
|
changed = true;
|
|
}
|
|
}
|
|
} while (changed);
|
|
|
|
cout << endl;
|
|
printSudoku(csp);
|
|
|
|
if (csp.isSatisfiable()) {
|
|
cout << "This sudoku has a solution ";
|
|
|
|
if (csp.hasUniqueSolution())
|
|
cout << "and the solution is unique\n";
|
|
else
|
|
cout << "but it wasn't possible to determine it univocally (missing values?)\n";
|
|
} else
|
|
cout << "This sudoku does not have any solution\n";
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|
|
|