The Constraint Programming Solver

Example 6.2 Alphabet Blocks Problem

This example illustrates the use of the global cardinality constraint (GCC). The alphabet blocks problem consists of finding an arrangement of letters on four alphabet blocks. Each alphabet block has a single letter on each of its six sides. Collectively, the four blocks contain every letter of the alphabet except Q and Z. By arranging the blocks in various ways, the following words should be spelled out: BAKE, ONYX, ECHO, OVAL, GIRD, SMUG, JUMP, TORN, LUCK, VINY, LUSH, and WRAP.

You can formulate this problem as a CSP by representing each of the 24 letters as an integer variable. The domain of each variable is the set $\{ 1, 2, 3, 4\} $, which represents block1 through block4. The assignment A = 1 indicates that the letter A is on a side of block1. Because each block has six sides, each value v in $\{ 1, 2, 3, 4\} $ must be assigned to exactly six variables so that each side of a block has a letter on it. This restriction can be formulated as a global cardinality constraint over all 24 variables, with common lower and upper bounds set equal to 6.

Moreover, in order to spell all the words listed previously, the four letters in each of the 12 words must be on different blocks. Another GCC statement that specifies 12 global cardinality constraints enforces these conditions. You can also formulate these restrictions by using 12 all-different constraints. Finally, four FIX statements break the symmetries that blocks are interchangeable. These constraints preset the blocks that contain the letters B, A, K, and E as block1, block2, block3, and block4, respectively.

The complete representation of the problem is as follows:

proc optmodel;
   /* Each letter except Q and Z is represented with a variable. */
   /* The domain of each variable is the set of 4 blocks,        */
   /*   or {1, 2, 3, 4} for short.                               */
   set LETTERS = / A B C D E F G H I J K L M N O P R S T U V W X Y /;
   var Block {LETTERS} integer >= 1 <= 4;
   set BLOCKS = 1..4;

   /* There are exactly 6 letters on each alphabet block */
   con SixLettersPerBlock:
      gcc(Block, setof {b in BLOCKS} <b,6,6>);

   /* The letters in each word must be on different blocks. */
   set WORDS = / BAKE ONYX ECHO OVAL GIRD SMUG JUMP TORN LUCK VINY LUSH WRAP /;
   con CanSpell {w in WORDS}:
      gcc({k in 1..length(w)} Block[char(w,k)], setof {b in BLOCKS} <b,0,1>);

   /* Note 2: These restrictions can also be enforced by ALLDIFF constraints:
   con CanSpellv2 {w in WORDS}:
      alldiff({k in 1..length(w)} Block[char(w,k)]);
   */

   /* Breaking the symmetry that blocks can be interchanged by setting
      the block that contains the letter B as block1, the block that
      contains the letter A as block2, etc. */
   for {k in 1..length('BAKE')} fix Block[char('BAKE',k)] = k;

   solve;
   print Block;
quit;

The solution to this problem is shown in Output 6.2.1.

Output 6.2.1: Solution to Alphabet Blocks Problem

The OPTMODEL Procedure

Problem Summary
Objective Sense Minimization
Objective Function (0)
Objective Type Constant
   
Number of Variables 24
Bounded Above 0
Bounded Below 0
Bounded Below and Above 20
Free 0
Fixed 4
Binary 1
Integer 23
   
Number of Constraints 13
Linear LE (<=) 0
Linear EQ (=) 0
Linear GE (>=) 0
Linear Range 0
Alldiff 0
Element 0
GCC 13
Lexico (<=) 0
Lexico (<) 0
Pack 0
Reify 0
   
Constraint Coefficients 0

Performance Information
Execution Mode Single-Machine
Number of Threads 1

Solution Summary
Solver CLP
Variable Selection MINR
Objective Function (0)
Solution Status Solution Limit Reached
Objective Value 0
   
Solutions Found 1
Presolve Time 0.00
Solution Time 0.00

[1] Block
A 2
B 1
C 2
D 2
E 4
F 1
G 4
H 3
I 1
J 2
K 3
L 4
M 3
N 2
O 1
P 4
R 3
S 2
T 4
U 1
V 3
W 1
X 3
Y 4