Download
/*
Crossfigure problem in Gecode.
CSPLib problem 21
http://www.csplib.org/Problems/prob021
"""
Crossfigures are the numerical equivalent of crosswords. You have a grid and some
clues with numerical answers to place on this grid. Clues come in several different
forms (for example: Across 1. 25 across times two, 2. five dozen, 5. a square number,
10. prime, 14. 29 across times 21 down ...).
"""
Also, see
http://en.wikipedia.org/wiki/Cross-figure
William Y. Sit: "On Crossnumber Puzzles and The Lucas-Bonaccio Farm 1998
http://scisun.sci.ccny.cuny.edu/~wyscc/CrossNumber.pdf
Bill Williams: Crossnumber Puzzle, The Little Pigley Farm
http://jig.joelpomerantz.com/fun/dogsmead.html
Compare with the following models:
* MiniZinc: http://www.hakank.org/minizinc/crossfigure.mzn
* Comet : http://www.hakank.org/comet/crossfigure.co
This Gecode model was created by Hakan Kjellerstrand (hakank@gmail.com)
Also, see my Gecode page: http://www.hakank.org/gecode/ .
*/
// Licenced under CC-BY-4.0 : http://creativecommons.org/licenses/by/4.0/
#include <gecode/driver.hh>
#include <gecode/int.hh>
#include <gecode/minimodel.hh>
using namespace Gecode;
using std::cout;
using std::endl;
void to_num(Space & space, IntVarArray x, IntVar z, int base = 10, IntConLevel icl = ICL_BND) {
int len = x.size();
IntArgs coeffs(len);
for(int r = 0; r < len; r++) {
coeffs[r] = pow(base, len-r-1);
}
// linear(space, coeffs, x, IRT_EQ, z, icl);
rel(space, sum(coeffs, x) == z, icl);
}
/*
across(Matrix, Across, Len, Row, Col)
Constrains 'Across' to be equal to the number represented by the
'Len' digits starting at position (Row, Col) of the array 'Matrix'
and proceeding across.
*/
void across(Space& space, IntVarArray matrix, IntVar Across, int Len, int Row, int Col, int n) {
IntVarArray tmp(space, Len, 0, 9999);
to_num(space, tmp, Across);
for(int i = 0; i < Len; i++) {
// also, convert to 0-based
rel(space, matrix[(Row-1)*n+(Col-1)+i] == tmp[i]);
}
}
/*
down(Matrix, Down, Len, Row, Col):
Constrains 'Down' to be equal to the number represented by the
'Len' digits starting at position (Row, Col) of the array 'Matrix'
and proceeding down.
*/
void down(Space& space, IntVarArray matrix, IntVar Down, int Len, int Row, int Col, int n) {
IntVarArray tmp(space, Len, 0, 9999);
to_num(space, tmp, Down);
for(int i = 0; i < Len; i++) {
// also, convert to 0-based
rel(space, matrix[(Row-1+i)*n+Col-1] == tmp[i]);
}
}
// is a square?
void is_square(Space& space, IntVar x) {
IntVar y(space, 0, 100);
rel(space, y*y == x);
}
// is a prime?
void is_prime(Space& space, IntVar x) {
for(int i = 2; i <= 99; i++) {
rel(space, (i < x) >> (x % i > 0));
}
}
// fix the black boxes and convert to 0-based
void fix_box(Space& space, IntVarArray x, int r, int c, int n) {
rel(space, x[(r-1)*n+(c-1)] == 0);
}
class Crossfigure : public Script {
protected:
static const int n = 9; // size of matrix
// matrix
IntVarArray x;
public:
;
Crossfigure(const SizeOptions& opt)
:
x(*this, n*n, 0, 9)
{
IntVar A1(*this, 0, 9999);
IntVar A4(*this, 0, 9999);
IntVar A7(*this, 0, 9999);
IntVar A8(*this, 0, 9999);
IntVar A9(*this, 0, 9999);
IntVar A10(*this, 0, 9999);
IntVar A11(*this, 0, 9999);
IntVar A13(*this, 0, 9999);
IntVar A15(*this, 0, 9999);
IntVar A17(*this, 0, 9999);
IntVar A20(*this, 0, 9999);
IntVar A23(*this, 0, 9999);
IntVar A24(*this, 0, 9999);
IntVar A25(*this, 0, 9999);
IntVar A27(*this, 0, 9999);
IntVar A28(*this, 0, 9999);
IntVar A29(*this, 0, 9999);
IntVar A30(*this, 0, 9999);
IntVar D1(*this, 0, 9999);
IntVar D2(*this, 0, 9999);
IntVar D3(*this, 0, 9999);
IntVar D4(*this, 0, 9999);
IntVar D5(*this, 0, 9999);
IntVar D6(*this, 0, 9999);
IntVar D10(*this, 0, 9999);
IntVar D12(*this, 0, 9999);
IntVar D14(*this, 0, 9999);
IntVar D16(*this, 0, 9999);
IntVar D17(*this, 0, 9999);
IntVar D18(*this, 0, 9999);
IntVar D19(*this, 0, 9999);
IntVar D20(*this, 0, 9999);
IntVar D21(*this, 0, 9999);
IntVar D22(*this, 0, 9999);
IntVar D26(*this, 0, 9999);
IntVar D28(*this, 0, 9999);
// Set up the constraints between the matrix elements and the
// clue numbers.
// Note: these are 1-based and is converted to 0-base in
// the function
across(*this, x, A1, 4, 1, 1, n);
across(*this, x, A4, 4, 1, 6, n);
across(*this, x, A7, 2, 2, 1, n);
across(*this, x, A8, 3, 2, 4, n);
across(*this, x, A9, 2, 2, 8, n);
across(*this, x, A10, 2, 3, 3, n);
across(*this, x, A11, 2, 3, 6, n);
across(*this, x, A13, 4, 4, 1, n);
across(*this, x, A15, 4, 4, 6, n);
across(*this, x, A17, 4, 6, 1, n);
across(*this, x, A20, 4, 6, 6, n);
across(*this, x, A23, 2, 7, 3, n);
across(*this, x, A24, 2, 7, 6, n);
across(*this, x, A25, 2, 8, 1, n);
across(*this, x, A27, 3, 8, 4, n);
across(*this, x, A28, 2, 8, 8, n);
across(*this, x, A29, 4, 9, 1, n);
across(*this, x, A30, 4, 9, 6, n);
down(*this, x, D1, 4, 1, 1, n);
down(*this, x, D2, 2, 1, 2, n);
down(*this, x, D3, 4, 1, 4, n);
down(*this, x, D4, 4, 1, 6, n);
down(*this, x, D5, 2, 1, 8, n);
down(*this, x, D6, 4, 1, 9, n);
down(*this, x, D10, 2, 3, 3, n);
down(*this, x, D12, 2, 3, 7, n);
down(*this, x, D14, 3, 4, 2, n);
down(*this, x, D16, 3, 4, 8, n);
down(*this, x, D17, 4, 6, 1, n);
down(*this, x, D18, 2, 6, 3, n);
down(*this, x, D19, 4, 6, 4, n);
down(*this, x, D20, 4, 6, 6, n);
down(*this, x, D21, 2, 6, 7, n);
down(*this, x, D22, 4, 6, 9, n);
down(*this, x, D26, 2, 8, 2, n);
down(*this, x, D28, 2, 8, 8, n);
// Set up the clue constraints.
// Across
// 1 27 across times two
// 4 4 down plus seventy-one
// 7 18 down plus four
// 8 6 down divided by sixteen
// 9 2 down minus eighteen
// 10 Dozen in six gross
// 11 5 down minus seventy
// 13 26 down times 23 across
// 15 6 down minus 350
// 17 25 across times 23 across
// 20 A square number
// 23 A prime number
// 24 A square number
// 25 20 across divided by seventeen
// 27 6 down divided by four
// 28 Four dozen
// 29 Seven gross
// 30 22 down plus 450
rel(*this, A1 == 2 * A27);
rel(*this, A4 == D4 + 71);
rel(*this, A7 == D18 + 4);
rel(*this, A8 == D6 / 16);
rel(*this, A9 == D2 - 18);
rel(*this, A10 == 6 * 144 / 12);
rel(*this, A11 == D5 - 70);
rel(*this, A13 == D26 * A23);
rel(*this, A15 == D6 - 350);
rel(*this, A17 == A25 * A23);
is_square(*this, A20);
is_prime(*this, A23);
is_square(*this, A24);
rel(*this, A25 == A20 / 17);
rel(*this, A27 == D6 / 4);
rel(*this, A28 == 4 * 12);
rel(*this, A29 == 7 * 144);
rel(*this, A30 == D22 + 450);
// Down
// 1 1 across plus twenty-seven
// 2 Five dozen
// 3 30 across plus 888
// 4 Two times 17 across
// 5 29 across divided by twelve
// 6 28 across times 23 across
// 10 10 across plus four
// 12 Three times 24 across
// 14 13 across divided by sixteen
// 16 28 down times fifteen
// 17 13 across minus 399
// 18 29 across divided by eighteen
// 19 22 down minus ninety-four
// 20 20 across minus nine
// 21 25 across minus fifty-two
// 22 20 down times six
// 26 Five times 24 across
// 28 21 down plus twenty-seven
rel(*this, D1 == A1 + 27);
rel(*this, D2 == 5 * 12);
rel(*this, D3 == A30 + 888);
rel(*this, D4 == 2 * A17);
rel(*this, D5 == A29 / 12);
rel(*this, D6 == A28 * A23);
rel(*this, D10 == A10 + 4);
rel(*this, D12 == A24 * 3);
rel(*this, D14 == A13 / 16);
rel(*this, D16 == 15 * D28);
rel(*this, D17 == A13 - 399);
rel(*this, D18 == A29 / 18);
rel(*this, D19 == D22 - 94);
rel(*this, D20 == A20 - 9);
rel(*this, D21 == A25 - 52);
rel(*this, D22 == 6 * D20);
rel(*this, D26 == 5 * A24);
rel(*this, D28 == D21 + 27);
// Fix the blackboxes
// Note: these are 1-based and is converted to 0-base in
// the function
fix_box(*this, x,1,5,n);
fix_box(*this, x,2,3,n);
fix_box(*this, x,2,7,n);
fix_box(*this, x,3,2,n);
fix_box(*this, x,3,5,n);
fix_box(*this, x,3,8,n);
fix_box(*this, x,4,5,n);
fix_box(*this, x,5,1,n);
fix_box(*this, x,5,3,n);
fix_box(*this, x,5,4,n);
fix_box(*this, x,5,5,n);
fix_box(*this, x,5,6,n);
fix_box(*this, x,5,7,n);
fix_box(*this, x,5,9,n);
fix_box(*this, x,6,5,n);
fix_box(*this, x,7,2,n);
fix_box(*this, x,7,5,n);
fix_box(*this, x,7,8,n);
fix_box(*this, x,8,3,n);
fix_box(*this, x,8,7,n);
fix_box(*this, x,9,5,n);
// branching
branch(*this, x, INT_VAR_DEGREE_MIN(), INT_VAL_MAX());
}
// Print solution
virtual void
print(std::ostream& os) const {
for(int r = 0; r < n; r++) {
for(int c = 0; c < n; c++) {
int t = x[r*n+c].val();
if (t != 0) {
os << t;
} else {
os << " ";
}
}
os << std::endl;
}
os << std::endl;
}
// Constructor for cloning s
Crossfigure(bool share, Crossfigure& s) : Script(share,s) {
x.update(*this, share, s.x);
}
// Copy during cloning
virtual Space*
copy(bool share) {
return new Crossfigure(share,*this);
}
};
int
main(int argc, char* argv[]) {
SizeOptions opt("Crossfigure");
opt.solutions(0);
opt.parse(argc,argv);
Script::run<Crossfigure,DFS,SizeOptions>(opt);
return 0;
}