/* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ /* * Main authors: * Mikael Lagerkvist * * Copyright: * Mikael Lagerkvist, 2006 * * Last modified: * $Date: 2011-05-11 20:44:17 +1000 (Wed, 11 May 2011) $ by $Author: tack $ * $Revision: 12001 $ * * This file is part of Gecode, the generic constraint * development environment: * http://www.gecode.org * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * */ #include #include #include #include using namespace Gecode; /** \name Constant sets for attacking queens. * * \relates QueenArmies */ IntSet* A; /** * \brief %Example: Peaceable co-existing armies of queens * * The goal of this problem is to place as many white and black queens * on a chess-board without any two queens of different color * attacking each other. The number of black queens should * be greater than or equal to the number of white queens. * * This model is based on the one presented in "Models and Symmetry * Breaking for 'Peaceable Armies of Queens'", by Barbara M. Smith, Karen * E. Petrie, and Ian P. Gent. * * The smart version uses a custom brancher implementing a heuristic * from the above paper, that helps speeding up the proof of * optimality. * * \ingroup Example * */ class QueenArmies : public MaximizeScript { public: const int n; SetVar U, ///< Set of un-attacked squares W; ///< Set of squares occupied by white queens BoolVarArray w, ///< The placement of the white queens b; ///< The placement of the black queens IntVar q; ///< The number of white queens placed. /// Branching to use for model enum { BRANCH_NAIVE, ///< Choose variables left to right BRANCH_SPECIFIC ///< Choose variable with problem specific strategy }; /// Search variants enum { SEARCH_BAB, ///< Use branch and bound to optimize SEARCH_RESTART ///< Use restart to optimize }; /// Constructor QueenArmies(const SizeOptions& opt) : n(opt.size()), U(*this, IntSet::empty, IntSet(0, n*n)), W(*this, IntSet::empty, IntSet(0, n*n)), w(*this, n*n, 0, 1), b(*this, n*n, 0, 1), q(*this, 0, n*n) { // Basic rules of the model for (int i = n*n; i--; ) { // w[i] means that no blacks are allowed on A[i] rel(*this, w[i] == (U || A[i])); // Make sure blacks and whites are disjoint. rel(*this, !w[i] || !b[i]); // If i in U, then b[i] has a piece. rel(*this, b[i] == (singleton(i) <= U)); } // Connect optimization variable to number of pieces linear(*this, w, IRT_EQ, q); linear(*this, b, IRT_GQ, q); // Connect cardinality of U to the number of black pieces. IntVar unknowns = expr(*this, cardinality(U)); rel(*this, q <= unknowns); linear(*this, b, IRT_EQ, unknowns); if (opt.branching() == BRANCH_NAIVE) { branch(*this, w, INT_VAR_NONE, INT_VAL_MAX); branch(*this, b, INT_VAR_NONE, INT_VAL_MAX); } else { QueenBranch::post(*this); assign(*this, b, INT_ASSIGN_MAX); } } /// Constructor for cloning QueenArmies(bool share, QueenArmies& s) : MaximizeScript(share,s), n(s.n) { U.update(*this, share, s.U); W.update(*this, share, s.W); w.update(*this, share, s.w); b.update(*this, share, s.b); q.update(*this, share, s.q); } /// Return copy during cloning virtual Space* copy(bool share) { return new QueenArmies(share,*this); } /// Return solution cost virtual IntVar cost(void) const { return q; } /// Print solution virtual void print(std::ostream& os) const { os << '\t'; for (int i = 0; i < n*n; ++i) { if (w[i].assigned() && w[i].val()) os << "W"; else if (b[i].assigned() && b[i].val()) os << "B"; else if (!w[i].assigned() && !b[i].assigned()) os << " "; else os << "."; if ((i+1)%n == 0) os << std::endl << (i!=(n*n-1)?"\t":""); } os << "Number of white queens: " << q << std::endl << std::endl; } /** \brief Custom brancher for Peacable queens * * Custom brancher that tries to place white queens so that they * maximise the amount of un-attacked squares that become attacked. * * \relates QueenArmies */ class QueenBranch : public Brancher { private: /// Cache of last computed decision mutable int start; /// Choice class Choice : public Gecode::Choice { public: /// Position of variable int pos; /// Value of variable bool val; /** Initialize choice for brancher \a b, position \a pos0, * and value \a val0. */ Choice(const Brancher& b, int pos0, bool val0) : Gecode::Choice(b,2), pos(pos0), val(val0) {} /// Report size occupied virtual size_t size(void) const { return sizeof(Choice); } /// Archive into \a e virtual void archive(Archive& e) const { Gecode::Choice::archive(e); e << pos << val; } }; /// Construct brancher QueenBranch(Home home) : Brancher(home), start(0) {} /// Constructor for cloning QueenBranch(Space& home, bool share, QueenBranch& b) : Brancher(home, share, b), start(b.start) {} public: /// Check status of brancher, return true if alternatives left. virtual bool status(const Space& home) const { const QueenArmies& q = static_cast(home); for (int i = start; i < q.n*q.n; ++i) if (!q.w[i].assigned()) { start = i; return true; } // No non-assigned orders left return false; } /// Return choice virtual Gecode::Choice* choice(Space& home) { const QueenArmies& q = static_cast(home); int maxsize = -1; int pos = -1; for (int i = start; i < q.n*q.n; ++i) { if (q.w[i].assigned()) continue; IntSetRanges ai(A[i]); SetVarUnknownRanges qU(q.U); Iter::Ranges::Inter r(ai, qU); int size = Iter::Ranges::size(r); if (size > maxsize) { maxsize = size; pos = i; } } assert(pos != -1); return new Choice(*this, pos, true); } /// Return choice virtual Choice* choice(const Space&, Archive& e) { int pos, val; e >> pos >> val; return new Choice(*this, pos, val); } /** \brief Perform commit for choice \a _c and * alternative \a a. */ virtual ExecStatus commit(Space& home, const Gecode::Choice& _c, unsigned int a) { QueenArmies& q = static_cast(home); const Choice& c = static_cast(_c); bool val = (a == 0) ? c.val : !c.val; return me_failed(Int::BoolView(q.w[c.pos]).eq(q, val)) ? ES_FAILED : ES_OK; } /// Copy brancher during cloning virtual Actor* copy(Space& home, bool share) { return new (home) QueenBranch(home, share, *this); } /// Post brancher static void post(QueenArmies& home) { (void) new (home) QueenBranch(home); } /// Delete brancher and return its size virtual size_t dispose(Space&) { return sizeof(*this); } }; }; /** \brief Position of a piece in a square board. * * \relates QueenArmies */ int pos(int i, int j, int n) { return i*n + j; } /** \brief Main-function * \relates QueenArmies */ int main(int argc, char* argv[]) { SizeOptions opt("QueenArmies"); opt.size(6); opt.branching(QueenArmies::BRANCH_SPECIFIC); opt.branching(QueenArmies::BRANCH_NAIVE, "naive"); opt.branching(QueenArmies::BRANCH_SPECIFIC, "specific"); opt.search(QueenArmies::SEARCH_BAB); opt.search(QueenArmies::SEARCH_BAB, "bab"); opt.search(QueenArmies::SEARCH_RESTART, "restart"); opt.solutions(0); opt.parse(argc,argv); // Set up the A-sets // A[i] will contain the values attacked by a queen at position i int n = opt.size(); A = new IntSet[n*n]; int *p = new int[std::max(n*n, 25)]; int pn = 0; for (int i = n; i--; ) { for (int j = n; j--; ) { int dir[][2] = { { 0, 1}, { 1, 1}, { 1, 0}, { 0, -1}, {-1, -1}, {-1, 0}, { 1, -1}, {-1, 1} }; p[pn++] = pos(i, j, n); for (int k = 8; k--; ) { for (int l = 0; l < n && 0 <= (i+l*dir[k][0]) && (i+l*dir[k][0]) < n && 0 <= (j+l*dir[k][1]) && (j+l*dir[k][1]) < n; ++l) { p[pn++] = pos(i+l*dir[k][0], j+l*dir[k][1], n); } } A[pos(i, j, n)] = IntSet(p, pn); pn = 0; } } delete [] p; switch (opt.search()) { case QueenArmies::SEARCH_BAB: MaximizeScript::run(opt); break; case QueenArmies::SEARCH_RESTART: MaximizeScript::run(opt); break; } return 0; } // STATISTICS: example-any