001    package aima.search.nqueens;
002    
003    import java.util.HashSet;
004    import java.util.List;
005    import java.util.Random;
006    import java.util.Set;
007    
008    import aima.basic.XYLocation;
009    import aima.search.framework.GoalTest;
010    import aima.search.informed.ga.FitnessFunction;
011    
012    /**
013     * A class whose purpose is to evaluate the fitness of NQueen individuals
014     * and to provide utility methods for translating between an NQueensBoard
015     * representation and the String representation used by the GeneticAlgorithm.
016     */
017    
018    /**
019     * @author Ciaran O'Reilly
020     * 
021     */
022    public class NQueensFitnessFunction implements FitnessFunction, GoalTest {
023    
024            private final NQueensGoalTest goalTest = new NQueensGoalTest();
025    
026            public NQueensFitnessFunction() {
027    
028            }
029    
030            //
031            // START - Interface FitnessFunction
032            public Double getValue(String individual) {
033                    double fitness = 0;
034    
035                    NQueensBoard board = getBoardForIndividual(individual);
036                    int boardSize = board.getSize();
037    
038                    // Calculate the number of non-attacking pairs of queens (refer to AIMA
039                    // page 117).
040                    List<XYLocation> qPositions = board.getQueenPositions();
041                    for (int fromX = 0; fromX < (boardSize - 1); fromX++) {
042                            for (int toX = fromX + 1; toX < boardSize; toX++) {
043                                    int fromY = qPositions.get(fromX).getYCoOrdinate();
044                                    boolean nonAttackingPair = true;
045                                    // Check right beside
046                                    int toY = fromY;
047                                    if (board.queenExistsAt(new XYLocation(toX, toY))) {
048                                            nonAttackingPair = false;
049                                    }
050                                    // Check right and above
051                                    toY = fromY - (toX - fromX);
052                                    if (toY >= 0) {
053                                            if (board.queenExistsAt(new XYLocation(toX, toY))) {
054                                                    nonAttackingPair = false;
055                                            }
056                                    }
057                                    // Check right and below
058                                    toY = fromY + (toX - fromX);
059                                    if (toY < boardSize) {
060                                            if (board.queenExistsAt(new XYLocation(toX, toY))) {
061                                                    nonAttackingPair = false;
062                                            }
063                                    }
064    
065                                    if (nonAttackingPair) {
066                                            fitness += 1.0;
067                                    }
068                            }
069                    }
070    
071                    return fitness;
072            }
073    
074            // END - Interface FitnessFunction
075            //
076    
077            //
078            // START - Interface GoalTest
079            public boolean isGoalState(Object state) {
080                    return goalTest.isGoalState(getBoardForIndividual((String) state));
081            }
082    
083            // END - Interface GoalTest
084            //
085    
086            public NQueensBoard getBoardForIndividual(String individual) {
087                    int boardSize = individual.length();
088                    NQueensBoard board = new NQueensBoard(boardSize);
089                    for (int i = 0; i < boardSize; i++) {
090                            int pos = Character
091                                            .digit(individual.charAt(i), individual.length());
092                            board.addQueenAt(new XYLocation(i, pos));
093                    }
094    
095                    return board;
096            }
097    
098            public String generateRandomIndividual(int boardSize) {
099                    StringBuffer ind = new StringBuffer();
100    
101                    assert (boardSize >= Character.MIN_RADIX && boardSize <= Character.MAX_RADIX);
102    
103                    for (int i = 0; i < boardSize; i++) {
104                            ind.append(Character.forDigit(new Random().nextInt(boardSize),
105                                            boardSize));
106                    }
107    
108                    return ind.toString();
109            }
110    
111            public Set<Character> getFiniteAlphabetForBoardOfSize(int size) {
112                    Set<Character> fab = new HashSet<Character>();
113    
114                    assert (size >= Character.MIN_RADIX && size <= Character.MAX_RADIX);
115    
116                    for (int i = 0; i < size; i++) {
117                            fab.add(Character.forDigit(i, size));
118                    }
119    
120                    return fab;
121            }
122    }