//============================================================================= // Copyright 2006-2010 Daniel W. Dyer // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. //============================================================================= package org.uncommons.watchmaker.examples.sudoku; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import org.testng.annotations.Test; import org.uncommons.watchmaker.examples.ExamplesTestUtils; import org.uncommons.watchmaker.framework.EvolutionaryOperator; /** * Unit test for Sudoku mutation operator. * @author Daniel Dyer */ public class SudokuRowMutationTest { /** * Tests to ensure that rows are still valid after mutation. Each row * should contain each value 1-9 exactly once. */ @Test public void testValidity() { EvolutionaryOperator<Sudoku> mutation = new SudokuRowMutation(8, 1); List<Sudoku> population = Arrays.asList(SudokuTestUtils.createSudoku(new int[][] { {1, 2, 8, 5, 4, 3, 9, 6, 7}, {7, 6, 4, 9, 2, 8, 5, 1, 3}, {3, 9, 5, 7, 6, 1, 2, 4, 8}, {6, 1, 9, 4, 8, 5, 7, 3, 2}, {5, 8, 3, 6, 7, 2, 1, 9, 4}, {4, 7, 2, 3, 1, 9, 8, 5, 6}, {8, 5, 1, 2, 3, 6, 4, 7, 9}, {9, 4, 6, 8, 5, 7, 3, 2, 1}, {2, 3, 7, 1, 9, 4, 6, 8, 5} })); final Set<Integer> counts = new HashSet<Integer>(Sudoku.SIZE); for (int i = 0; i < 20; i++) { population = mutation.apply(population, ExamplesTestUtils.getRNG()); assert population.size() == 1 : "Population size changed after mutation(s)."; Sudoku mutatedSudoku = population.get(0); for (int j = 0; j < Sudoku.SIZE; j++) { Sudoku.Cell[] row = mutatedSudoku.getRow(j); assert row.length == Sudoku.SIZE : "Row length is invalid: " + row.length; for (Sudoku.Cell cell : row) { int value = cell.getValue(); assert value > 0 && value <= Sudoku.SIZE : "Cell value out of range: " + value; counts.add(value); } assert counts.size() == Sudoku.SIZE : "Row contains duplicates."; counts.clear(); } } } /** * Check that the mutation never modifies the value of fixed cells. */ @Test(dependsOnMethods = "testValidity") public void testFixedConstraints() { EvolutionaryOperator<Sudoku> mutation = new SudokuRowMutation(8, 1); Sudoku.Cell[][] cells = new Sudoku.Cell[Sudoku.SIZE][Sudoku.SIZE]; // One cell in each row is fixed (cell 1 in row 1, cell 2 in row 2, etc.) for (int row = 0; row < Sudoku.SIZE; row++) { for (int column = 0; column < Sudoku.SIZE; column++) { cells[row][column] = new Sudoku.Cell(column + 1, column == row); } } List<Sudoku> population = Arrays.asList(new Sudoku(cells)); for (int i = 0; i < 100; i++) // 100 generations of mutation. { population = mutation.apply(population, ExamplesTestUtils.getRNG()); Sudoku sudoku = population.get(0); for (int row = 0; row < Sudoku.SIZE; row++) { for (int column = 0; column < Sudoku.SIZE; column++) { if (row == column) { assert sudoku.isFixed(row, column) : "Fixed cell has become unfixed."; assert sudoku.getValue(row, column) == (row + 1) : "Fixed cell has changed value."; } else { assert !sudoku.isFixed(row, column) : "Unfixed cell has become fixed."; } } } } } @Test(expectedExceptions = IllegalArgumentException.class) public void testInvalidMutationCount() { new SudokuRowMutation(0, 1); // Should throw an IllegalArgumentException. } @Test(expectedExceptions = IllegalArgumentException.class) public void testInvalidMutationAmount() { new SudokuRowMutation(1, 0); // Should throw an IllegalArgumentException. } }