Courses/CS 460/Fall 2006/Strom, Richard/Choco Example - SendMoreMoney

From CSWiki

Jump to: navigation, search

[edit] Choco Example - Send More Money

Choco is pretty nice. It is actually easier to build constraints so far. You can download it here. It also lets you have real number variables.

This is the SendMoreMoney problem ported to Choco. The code might not be that clear so I'll explain the basics here.

The central object is the Problem object, somewhat analogous to the store in JaCoP. The difference is that variables (IntVar is the equivalent of FDV) and constraints aren't created and then added to the store, they are created by factories in the Problem object, then added to the Problem with the post function, which is the same as impose in JaCoP.

So for example, if you have a Problem object called myProb and you want to add two variables A and B with domain [0,9] and a constraint that they aren't equal, you would do the following:

Problem myProb = new Problem();

IntVar [] vars = new IntVar[2];

vars[0] = myProb.makeEnumIntVar("A", 0, 9);
vars[1] = myProb.makeEnumIntVar("B", 0, 9);

myProb.post(myProb.neq ( vars[0], vars[1] ) );

Solving is also done through the Problem object:

if (myProb.solve() == Boolean.TRUE) {
    for(int i = 0; i < myProb.getNbIntVars(); i++) {
        System.out.println(myProb.getIntVar(i) + " = " + ((IntDomainVar) myProb.getIntVar(i)).getVal());
    }
}

If you expect additional solutions, you can keep calling myProb.nextSolution() in a loop to get them all. There is also a Solver class which you can use to customize the search.

In the code below, the scalar factory function is used to do the SEND = 1000*S + 100*E + 10*N + D constraint. I commented out an uglier way I was trying to do it with the plus and mult functions so you can see how those work.

[edit] Code

import choco.integer.*;
import choco.*;

public class SendMoreMoneyChoco {
    
    public static void main(String [] args) {
        Problem SMM = new Problem();
        
        IntVar [] digits = new IntVar[8];                       
        digits[0] = SMM.makeEnumIntVar("S", 0, 9);
        digits[1] = SMM.makeEnumIntVar("E", 0, 9);
        digits[2] = SMM.makeEnumIntVar("N", 0, 9);
        digits[3] = SMM.makeEnumIntVar("D", 0, 9);
        digits[4] = SMM.makeEnumIntVar("M", 0, 9);
        digits[5] = SMM.makeEnumIntVar("O", 0, 9);
        digits[6] = SMM.makeEnumIntVar("R", 0, 9);
        digits[7] = SMM.makeEnumIntVar("Y", 0, 9);
        
        IntVar [] addends = new IntVar[3];
        addends[0] = SMM.makeEnumIntVar("SEND",  0, 9999);
        addends[1] = SMM.makeEnumIntVar("MORE",  0, 9999);
        addends[2] = SMM.makeEnumIntVar("MONEY", 0, 99999);
        
        // All Different
        SMM.post(SMM.allDifferent(digits));
        
        // S != 0, M != 0
        SMM.post(SMM.neq(digits[0], 0));
        SMM.post(SMM.neq(digits[4], 0));
        
        // 1000*S + 100*E + 10*N + D
        //SMM.post( SMM.eq( addends[0], SMM.plus ( SMM.mult(1000, digits[0]), SMM.plus( SMM.mult(100, digits[1]), SMM.plus( SMM.mult(10, digits[2]), digits[3] ) ) ) ) ) ;
        //SMM.post( SMM.eq( addends[1], SMM.plus ( SMM.mult(1000, digits[4]), SMM.plus( SMM.mult(100, digits[5]), SMM.plus( SMM.mult(10, digits[6]), digits[1] ) ) ) ) ) ;
        //SMM.post( SMM.eq( addends[2], SMM.plus( SMM.mult(10000, digits[4]), SMM.plus ( SMM.mult(1000, digits[5]), SMM.plus( SMM.mult(100, digits[2]), SMM.plus( SMM.mult(10, digits[1]), digits[7] ) ) ) ) ) );
        
        // two scalar arrays to build the sum constraints (1000*S + 100*E + 10*N + D et al.)
        int [] scalarA = new int[4];
        int [] scalarB = new int[5];
        
        scalarB [4] = 1;        scalarA [3] = 1;
        scalarB [3] = 10;       scalarA [2] = 10;
        scalarB [2] = 100;      scalarA [1] = 100;
        scalarB [1] = 1000;     scalarA [0] = 1000;
        scalarB [0] = 10000;
        
        // Three IntVar arrays to use with the scalar constraint
        IntVar[] send  = new IntVar[4];
        IntVar[] more  = new IntVar[4];
        IntVar[] money = new IntVar[5];
        
        send[0] = digits[0];    more[0] = digits[4];    money[0] = digits[4];
        send[1] = digits[1];    more[1] = digits[5];    money[1] = digits[5];
        send[2] = digits[2];    more[2] = digits[6];    money[2] = digits[2];
        send[3] = digits[3];    more[3] = digits[1];    money[3] = digits[1];
                                                        money[4] = digits[7];
                                                        
        SMM.post(SMM.eq( SMM.scalar(send,  scalarA), addends[0] ) );
        SMM.post(SMM.eq( SMM.scalar(more,  scalarA), addends[1] ) );
        SMM.post(SMM.eq( SMM.scalar(money, scalarB), addends[2] ) );
        
        // Finally, the SEND + MORE = MONEY constraint
        SMM.post( SMM.eq( addends[2], SMM.plus( addends[0], addends[1] ) ) );
        
        // Solve
        if (SMM.solve() == Boolean.TRUE) {
            for(int i = 0; i < SMM.getNbIntVars(); i++) {
                System.out.println(SMM.getIntVar(i) + " = " + ((IntDomainVar) SMM.getIntVar(i)).getVal());
            }
        }
    }
}

[edit] Output

S:9 = 9
E:5 = 5
N:6 = 6
D:7 = 7
M:1 = 1
O:0 = 0
R:8 = 8
Y:2 = 2
SEND:9567 = 9567
MORE:1085 = 1085
MONEY:10652 = 10652