Download
% 
% Crossfigure problem in MiniZinc.
%
% 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

% 
% This MiniZinc model was created by Hakan Kjellerstrand, hakank@bonetmail.com
% See also my MiniZinc page: http://www.hakank.org/minizinc
%

% Licenced under CC-BY-4.0 : http://creativecommons.org/licenses/by/4.0/

%
% This model was inspired by the ECLiPSe model written by Warwick Harvey:
% http://www.csplib.org/Problems/prob021/models
%
%
% Data from 
% http://thinks.com/crosswords/xfig.htm.
%
% This problem is 001 from http://thinks.com/crosswords/xfig.htm 
% ("X" is the blackbox and is fixed to the value of 0)
%
% 1  2  3  4  5  6  7  8  9
% ---------------------------
% 1  2  _  3  X  4  _  5  6  % 1
% 7  _  X  8  _  _  X  9  _  % 2
% _  X  10 _  X  11 12 X  _  % 3
% 13 14 _  _  X  15 _  16 _  % 4 
% X  _  X  X  X  X  X  _  X  % 5 
% 17 _  18 19 X  20 21 _ 22  % 6
% _  X  23 _  X  24 _  X  _  % 7
% 25 26 X  27 _  _  X  28 _  % 8
% 29 _  _  _  X  30 _  _  _  % 9

%
% The answer is
%  1608 9183
%  60 201 42
%  3 72 14 1
%  5360 2866
%   3     4
%  4556 1156
%  9 67 16 8
%  68 804 48
%  1008 7332

% Solutions:
% MiniZinc and Gecode/fz solves the problem in about 8 seconds.
% ECLiPSe/ic: 35 seconds
% MiniZinc/fdmip in 14 seconds.
%


int: n = 9;
array[1..n, 1..n] of var 0..9: M;

set of int: D = 0..9999; % the max length of the numbers in this problem is 4
var D: A1;
var D: A4;
var D: A7;
var D: A8;
var D: A9;
var D: A10;
var D: A11;
var D: A13;
var D: A15;
var D: A17;
var D: A20;
var D: A23;
var D: A24;
var D: A25;
var D: A27;
var D: A28;
var D: A29;
var D: A30;

var D: D1;
var D: D2;
var D: D3;
var D: D4;
var D: D5;
var D: D6;
var D: D10;
var D: D12;
var D: D14;
var D: D16;
var D: D17;
var D: D18;
var D: D19;
var D: D20;
var D: D21;
var D: D22;
var D: D26;
var D: D28;


%
% 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.
%
predicate across(array[int, int] of var D: Matrix, var D: Across, int: Len, int: Row, int: Col) =
   let {
     array[1..Len] of var D: tmp
   }
   in
   toNum10(tmp, Across)
   /\
   forall(i in 0..Len-1) (

       Matrix[Row,Col+i] = tmp[i+1]
   )
;

%
% 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.
%
predicate down(array[int,int] of var D: Matrix, var D: Down, int: Len, int: Row, int: Col) =
   let {
     array[1..Len] of var D: tmp
   }
   in
   toNum10(tmp, Down)
   /\
   forall(i in 0..Len-1) (
      Matrix[Row+i,Col] = tmp[i+1]
   )
;


%
% converts a number <-> array
%
predicate toNum10(array[int] of var D: a, var D: n) =
          let { int: len = length(a) }
          in
          n = sum(i in 1..len) (
            ceil(pow(10.0, int2float(len-i))) * a[i]
          )
          /\ forall(i in 1..len) (a[i] >= 0)
;


%
% x is a square
% 
predicate square(var D: x) =
   exists(y in D) (
      y*y = x
   )
;


%
% very simple primality test
%
predicate is_prime(var int: x) =
   forall(i in 2..ceil(sqrt(9999.0))) ( 
        (i < x) -> (x mod i > 0)
   )
;


solve :: int_search(
         [M[i,j] | i,j in 1..n] ++
         [A1,A4,A7,A8,A9,A10,A11,A13,A15,A17,A20,A23,A24,A25,A27,A28,A29,A30,
         D1,D2,D3,D4,D5,D6,D10,D12,D14,D16,D17,D18,D19,D20,D21,D22,D26,D28],
         occurrence,
         indomain_min,
         complete
         ) 
     satisfy;


constraint

   % Set up the constraints between the matrix elements and the
   % clue numbers.
   across(M, A1, 4, 1, 1)  /\ 
   across(M, A4, 4, 1, 6)  /\ 
   across(M, A7, 2, 2, 1)  /\ 
   across(M, A8, 3, 2, 4)  /\ 
   across(M, A9, 2, 2, 8)  /\ 
   across(M, A10, 2, 3, 3) /\ 
   across(M, A11, 2, 3, 6) /\ 
   across(M, A13, 4, 4, 1) /\ 
   across(M, A15, 4, 4, 6) /\ 
   across(M, A17, 4, 6, 1) /\ 
   across(M, A20, 4, 6, 6) /\ 
   across(M, A23, 2, 7, 3) /\ 
   across(M, A24, 2, 7, 6) /\ 
   across(M, A25, 2, 8, 1) /\ 
   across(M, A27, 3, 8, 4) /\ 
   across(M, A28, 2, 8, 8) /\ 
   across(M, A29, 4, 9, 1) /\ 
   across(M, A30, 4, 9, 6) /\ 

   down(M, D1, 4, 1, 1)  /\ 
   down(M, D2, 2, 1, 2)  /\ 
   down(M, D3, 4, 1, 4)  /\ 
   down(M, D4, 4, 1, 6)  /\ 
   down(M, D5, 2, 1, 8)  /\ 
   down(M, D6, 4, 1, 9)  /\ 
   down(M, D10, 2, 3, 3) /\ 
   down(M, D12, 2, 3, 7) /\ 
   down(M, D14, 3, 4, 2) /\ 
   down(M, D16, 3, 4, 8) /\ 
   down(M, D17, 4, 6, 1) /\ 
   down(M, D18, 2, 6, 3) /\ 
   down(M, D19, 4, 6, 4) /\ 
   down(M, D20, 4, 6, 6) /\ 
   down(M, D21, 2, 6, 7) /\ 
   down(M, D22, 4, 6, 9) /\ 
   down(M, D26, 2, 8, 2) /\ 
   down(M, D28, 2, 8, 8) /\ 


   % 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 

   A1 = 2 * A27         /\ 
   A4 = D4 + 71         /\ 
   A7 = D18 + 4         /\ 
   A8 = D6 div 16       /\ 
   A9 = D2 - 18         /\ 
   A10 = 6 * 144 div 12 /\ 
   A11 = D5 - 70        /\ 
   A13 = D26 * A23      /\ 
   A15 = D6 - 350       /\ 
   A17 = A25 * A23      /\ 
   square(A20)          /\ 
   is_prime(A23)        /\
   square(A24)          /\ 
   A25 = A20 div 17     /\ 
   A27 = D6 div 4       /\ 
   A28 = 4 * 12         /\ 
   A29 = 7 * 144        /\ 
   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 

   D1 = A1 + 27     /\ 
   D2 = 5 * 12      /\ 
   D3 = A30 + 888   /\ 
   D4 = 2 * A17     /\ 
   D5 = A29 div 12  /\ 
   D6 = A28 * A23   /\ 
   D10 = A10 + 4    /\ 
   D12 = A24 * 3    /\ 
   D14 = A13 div 16 /\ 
   D16 = 15 * D28   /\ 
   D17 = A13 - 399  /\ 
   D18 = A29 div 18 /\ 
   D19 = D22 - 94   /\ 
   D20 = A20 - 9    /\ 
   D21 = A25 - 52   /\ 
   D22 = 6 * D20    /\ 
   D26 = 5 * A24    /\ 
   D28 = D21 + 27


   % Fix the blackboxes
   /\
   M[1,5] = 0 /\
   M[2,3] = 0 /\
   M[2,7] = 0 /\
   M[3,2] = 0 /\
   M[3,5] = 0 /\
   M[3,8] = 0 /\
   M[4,5] = 0 /\
   M[5,1] = 0 /\
   M[5,3] = 0 /\
   M[5,4] = 0 /\
   M[5,5] = 0 /\
   M[5,6] = 0 /\
   M[5,7] = 0 /\
   M[5,9] = 0 /\
   M[6,5] = 0 /\
   M[7,2] = 0 /\
   M[7,5] = 0 /\
   M[7,8] = 0 /\
   M[8,3] = 0 /\
   M[8,7] = 0 /\
   M[9,5] = 0
;


output [
 show([A1,A4,A7,A8,A9,A10,A11,A13,A15,A17,A20,A23,A24,A25,A27,A28,A29,A30,
       D1,D2,D3,D4,D5,D6,D10,D12,D14,D16,D17,D18,D19,D20,D21,D22,D26,D28]), "\n",
] ++ 
[
  if j = 1 then "\n" else " " endif ++
    show(M[i,j])
  | i,j  in 1..n
] ++ ["\n"];