/* * ===================================================================================== * * Filename: fourcolours.cpp * * Description: Interactively select a colour for some European countries. At each * selection, the domains of colours for remaining countries are * restricted, under the constraint "two adjacent countries cannot have * the same colour" * * Complile: g++ -IPATH/TO/csp++.h -o fourcolours fourcolours.cpp * Version: 1.0 * Created: 17/05/2010 09:22:25 * Revision: none * Compiler: gcc * * Author: BlackLight (http://0x00.ath.cx), * Licence: GNU GPL v.3 * Company: lulz * * ===================================================================================== */ #include #include #include #include #include #include using namespace std; typedef enum { I, CH, DK, D, A, F, B, E, P, L, NL, } Country; typedef enum { red, green, blue, yellow } Colour; #define COUNTRIES 11 #define COLOURS 4 static const char* countries[COUNTRIES] = { "Italy", "Switz.", "Denmark", "Germany", "Austria", "France", "Belgium", "Spain", "Portugal", "Luxemb.", "Holland", }; static const char* colours[COLOURS] = { "red", "green", "blue", "yellow" }; /** * FUNCTION: c * * Constraint function, it expresses the logic of the constraint. In our case, * the adjoining countries must have different colours. Each colours condition * is verified only if both the values have been set ("fixed" field in CSPvariable * structure), in our case by the user, in order to avoid an inconstistent CSP * from random values found inside of the variables before the initialization */ bool c ( std::vector< CSPvariable > variables) { return ( ( !(variables[I] .fixed || variables[CH].fixed) || (variables[I] .value != variables[CH].value) ) && ( !(variables[I] .fixed || variables[A] .fixed) || (variables[I] .value != variables[A] .value) ) && ( !(variables[I] .fixed || variables[F] .fixed) || (variables[I] .value != variables[F] .value) ) && ( !(variables[A] .fixed || variables[CH].fixed) || (variables[A] .value != variables[CH].value) ) && ( !(variables[A] .fixed || variables[D] .fixed) || (variables[A] .value != variables[D] .value) ) && ( !(variables[CH].fixed || variables[D] .fixed) || (variables[CH].value != variables[D] .value) ) && ( !(variables[CH].fixed || variables[F] .fixed) || (variables[CH].value != variables[F] .value) ) && ( !(variables[D] .fixed || variables[F] .fixed) || (variables[D] .value != variables[F] .value) ) && ( !(variables[E] .fixed || variables[F] .fixed) || (variables[E] .value != variables[F] .value) ) && ( !(variables[E] .fixed || variables[P] .fixed) || (variables[E] .value != variables[P] .value) ) && ( !(variables[B] .fixed || variables[F] .fixed) || (variables[B] .value != variables[F] .value) ) && ( !(variables[B] .fixed || variables[D] .fixed) || (variables[B] .value != variables[D] .value) ) && ( !(variables[B] .fixed || variables[NL].fixed) || (variables[B] .value != variables[NL].value) ) && ( !(variables[B] .fixed || variables[L] .fixed) || (variables[B] .value != variables[L] .value) ) && ( !(variables[L] .fixed || variables[F] .fixed) || (variables[L] .value != variables[F] .value) ) && ( !(variables[L] .fixed || variables[D] .fixed) || (variables[L] .value != variables[D] .value) ) && ( !(variables[NL].fixed || variables[D] .fixed) || (variables[NL].value != variables[D] .value) ) && ( !(variables[DK].fixed || variables[D] .fixed) || (variables[DK].value != variables[D] .value) ) ); } /** * FUNCTION: printDomain * * Given the CSP and the index of the variable, prints its allowed domain */ void printDomain (CSP csp, int variable) { cout << "[ "; for ( size_t i=0; i < csp.domain(variable).size(); i++ ) { cout << colours[csp.domain(variable)[i]]; if ( i < csp.domain(variable).size() - 1 ) cout << ", "; } cout << " ]"; } /** * FUNCTION: printDomains * * Given the CSP, prints the domains of all the variables */ void printDomains (CSP csp) { for ( size_t i=0; i < csp.size(); i++) { cout << "Domain for variable '" << countries[i] << "':\t"; printDomain(csp, i); cout << endl; } cout << endl; if (csp.isSatisfiable()) { cout << "The CSP has a solution\n"; if (csp.hasUniqueSolution()) cout << "The solution is unique\n"; } else cout << "The CSP does not have a solution\n"; } /** * FUNCTION: getIndexByColour * * Given a colour as string, it picks up the associated index, * if the colour exists in the list, otherwise it throws an * evil exception */ size_t getIndexByColour ( const char* colour ) { for ( size_t i=0; i < COLOURS; i++ ) { if (!strcmp(colours[i], colour)) return i; } throw 666; } /** * FUNCTION: valueOK * * Given the CSP, the index of a variable and a value, it checks * if the given value is consistent to the domain of that variable */ bool valueOK ( CSP csp, size_t variable, Colour value ) { for ( size_t i=0; i < csp.domain(variable).size(); i++ ) { if (csp.domain(variable)[i] == value) return true; } return false; } int main ( int argc, char *argv[] ) { vector domain; // Create the domain containing all the allowed colours for ( int i=0; i < COLOURS; i++) domain.push_back((Colour) i); // The CSP will contain as many variables as the number of countries, // applying the logical constraint specified in "c" function CSP csp(COUNTRIES, c); // Set the domain for the variables for ( size_t i=0; i < COUNTRIES; i++ ) csp.setDomain(i, domain); // Repeat until we don't find a unique solution for the CSP while (!csp.hasUniqueSolution()) { for ( size_t i=0; i < COUNTRIES && !csp.hasUniqueSolution(); i++ ) { bool satisfiable = true; // If a variable has an empty domain, something went wrong if (csp.domain(i).size() == 0) { cout << "No values left for country " << countries[i] << ", the domain was probably too small (few colours) or the problem is unsatisfiable\n"; return EXIT_FAILURE; } // If a variable has a domain containing an only element, // just choose it without annoying the user if (csp.domain(i).size() == 1) { cout << "Setting colour " << colours[csp.domain(i)[0]] << " for " << countries[i] << endl; csp.setValue( i, csp.domain(i)[0] ); csp.refreshDomains(); continue; } // Repeat the loop until the user inserts a valid colour for the CSP do { bool found; size_t col_index; // Repeat the loop until the users inserts a colour in the list do { string colour; cout << "Insert a colour for country '" << countries[i] << "' between " << endl << "\t"; printDomain(csp, i); cout << ": "; getline (cin, colour); try { col_index = getIndexByColour(colour.c_str()); found = true; } catch (int e) { cout << "Invalid input, please try again\n"; found = false; } } while (!found); // Check if the inserted colour is valid for the domain of the variable satisfiable = valueOK(csp, i, (Colour) col_index); if (!satisfiable) { cout << "You made an invalid choice according to the current constraints, please try again\n"; } else { csp.setValue(i, (Colour) col_index); csp.refreshDomains(); } } while (!satisfiable); } } cout << endl; printDomains(csp); return EXIT_SUCCESS; }