diff --git a/Main.java b/Main.java index 8fc9910..398bfe4 100644 --- a/Main.java +++ b/Main.java @@ -51,20 +51,22 @@ public class Main { // Choosing variables for different sudoku difficulties if (mutablePositions.size() < EASYTHRESHOLD) { // Easy sudoku - POPULATIONSIZE = 100; - TOURNAMENTSIZE = 5; - MUTATIONRATE = 0.05; + POPULATIONSIZE = 100000; + TOURNAMENTSIZE = 8; + MUTATIONRATE = 0.34; } else if (mutablePositions.size() < HARDTHRESHOLD) { // Hard sudoku - POPULATIONSIZE = 100; - TOURNAMENTSIZE = 5; - MUTATIONRATE = 0.057; + POPULATIONSIZE = 100000; + TOURNAMENTSIZE = 8; + MUTATIONRATE = 0.34; } else { // Ultra-hard sudoku - POPULATIONSIZE = 20000; + POPULATIONSIZE = 250000; TOURNAMENTSIZE = 4; - MUTATIONRATE = 0.032; + MUTATIONRATE = 0.15; } + + // Generate initial population of 100 chromosomes mainInstance.generateInitialChromosomes(POPULATIONSIZE, baseSudoku, mutablePositions); diff --git a/other/.LCKMain.java~ b/other/.LCKMain.java~ new file mode 100644 index 0000000..a273035 --- /dev/null +++ b/other/.LCKMain.java~ @@ -0,0 +1 @@ +/home/emil/Coding/Assignments/ITAI/Sudoku_solver/other/Main.java \ No newline at end of file diff --git a/other/Main.java b/other/Main.java new file mode 100644 index 0000000..06e48f9 --- /dev/null +++ b/other/Main.java @@ -0,0 +1,368 @@ +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +public class Main { + // Initalizing variables + public static int POPULATIONSIZE = 0; + public static int TOURNAMENTSIZE = 0; + public static double MUTATIONRATE = 0; + // Threshold of number of mutable positions for easy level sudoku + public static int EASYTHRESHOLD = 60; + public static int HARDTHRESHOLD = 70; + // List to store the population of chromosomes + List population = new ArrayList<>(); + Random random = new Random(); + + public static void main(String[] args) { + // Base Sudoku matrix (input matrix) + int[][] baseSudoku = new int[9][9]; + // List to track positions in Sudoku that are mutable + List mutablePositions = new ArrayList<>(); + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + + try { + // Reading the Sudoku matrix from the console input + for (int i = 0; i < 9; i++) { + // Split the input by space + String[] tokens = reader.readLine().split(" "); + for (int j = 0; j < 9; j++) { + if (tokens[j].equals("-")) { + // Empty cells are marked as 0 + baseSudoku[i][j] = 0; + // Add mutable positions (i, j) to the list + mutablePositions.add(new int[]{i, j}); + } else { + // Set fixed value from the input + baseSudoku[i][j] = Integer.parseInt(tokens[j]); + } + } + } + } catch (IOException e) { + // If an error occurs during input reading, print the error and stop the program + System.err.println("Error reading input: " + e.getMessage()); + return; + } + + Main mainInstance = new Main(); // Create instance of Main class + + // Choosing variables for different sudoku difficulties + if (mutablePositions.size() < EASYTHRESHOLD) { // Easy sudoku + POPULATIONSIZE = 100; + TOURNAMENTSIZE = 5; + MUTATIONRATE = 0.05; + } else if (mutablePositions.size() < HARDTHRESHOLD) { // Hard sudoku + POPULATIONSIZE = 100; + TOURNAMENTSIZE = 5; + MUTATIONRATE = 0.057; + } + else { // Ultra-hard sudoku + POPULATIONSIZE = 20000; + TOURNAMENTSIZE = 4; + MUTATIONRATE = 0.032; + } + + // Generate initial population of 100 chromosomes + mainInstance.generateInitialChromosomes(POPULATIONSIZE, baseSudoku, mutablePositions); + + Chromosome bestSolution = null; + + int generation = 0; // Track the number of generations + while (true) { + // Evaluate the fitness of each chromosome in the population + mainInstance.evaluatePopulation(); + + List newPopulation = new ArrayList<>(); + for (int i = 0; i < mainInstance.population.size() / 2; i++) { + // Select parents using tournament selection + List parents = mainInstance.tournamentSelection(TOURNAMENTSIZE); + // Perform crossover to create two children from the selected parents + Chromosome child1 = mainInstance.crossoverBySubgrids(parents.get(0), parents.get(1)); + Chromosome child2 = mainInstance.crossoverBySubgrids(parents.get(1), parents.get(0)); + + // Apply mutation to both children + mainInstance.mutateChromosome(child1, MUTATIONRATE); + mainInstance.mutateChromosome(child2, MUTATIONRATE); + + // Add both children to the new population + newPopulation.add(child1); + newPopulation.add(child2); + } + + // Replace the old population with the new population + mainInstance.population = newPopulation; + + // Get the best chromosome from the current population + bestSolution = mainInstance.getBestChromosome(); + generation++; + + // If the best solution found has a fitness of 0, print it and end the program + if (bestSolution.getFitness() == 0) { + bestSolution.printChromosome(false); + return; + } + } + } + + // Generate initial population of chromosomes + public void generateInitialChromosomes(int numberOfChromosomes, int[][] baseSudoku, List mutablePositions) { + for (int i = 0; i < numberOfChromosomes; i++) { + // Create a copy of the base Sudoku + int[][] sudoku = copyMatrix(baseSudoku); + // Randomly fill mutable positions + for (int[] pos : mutablePositions) { + int row = pos[0]; + int col = pos[1]; + sudoku[row][col] = random.nextInt(9) + 1; + } + // Create a new chromosome with the generated Sudoku and mutable positions + Chromosome chromosome = new Chromosome(sudoku, new ArrayList<>(mutablePositions)); + chromosome.evaluateFitness(); // Evaluate its fitness + population.add(chromosome); // Add to the population + } + } + + // Create a deep copy of a matrix + private int[][] copyMatrix(int[][] original) { + int[][] copy = new int[original.length][original[0].length]; + for (int i = 0; i < original.length; i++) { + System.arraycopy(original[i], 0, copy[i], 0, original[i].length); + } + return copy; + } + + + public List tournamentSelection(int tournamentSize) { + List selectedParents = new ArrayList<>(); + for (int i = 0; i < 2; i++) { + List tournament = new ArrayList<>(); + // Randomly select chromosomes for the tournament + for (int j = 0; j < tournamentSize; j++) { + Chromosome randomChromosome = population.get(random.nextInt(population.size())); + tournament.add(randomChromosome); + } + // Determine the best chromosome in the tournament based on fitness + Chromosome best = tournament.get(0); + for (Chromosome chromosome : tournament) { + if (chromosome.getFitness() < best.getFitness()) { + best = chromosome; + } + } + // Add the best chromosome to the list of selected parents + selectedParents.add(best); + } + return selectedParents; + } + + public Chromosome crossoverBySubgrids(Chromosome parent1, Chromosome parent2) { + int[][] childSudoku = new int[9][9]; + + // Copy the entire Sudoku grid from parent1 to the child + for (int row = 0; row < 9; row++) { + System.arraycopy(parent1.getSudoku()[row], 0, childSudoku[row], 0, 9); + } + + // Determine the number of subgrids to swap from parent2 to child (1-5) + int numSubgridsToSwap = random.nextInt(5) + 1; + List selectedSubgrids = new ArrayList<>(); + while (selectedSubgrids.size() < numSubgridsToSwap) { + int subgridIndex = random.nextInt(9); + if (!selectedSubgrids.contains(subgridIndex)) { + selectedSubgrids.add(subgridIndex); + } + } + + // Swap the selected subgrids from parent2 into the child + for (int subgrid : selectedSubgrids) { + int rowStart = (subgrid / 3) * 3; + int colStart = (subgrid % 3) * 3; + for (int row = rowStart; row < rowStart + 3; row++) { + for (int col = colStart; col < colStart + 3; col++) { + childSudoku[row][col] = parent2.getSudoku()[row][col]; + } + } + } + + // Create a new chromosome with the resulting child Sudoku and evaluate its fitness + Chromosome child = new Chromosome(childSudoku, parent1.getMutablePositions()); + child.evaluateFitness(); + return child; + } + + public void printPopulation(boolean printMutPos) { + System.out.println("Generated Population:"); + int count = 1; + // Print each chromosome's Sudoku and fitness value + for (Chromosome chromosome : population) { + System.out.println("Chromosome " + count + ":"); + chromosome.printChromosome(printMutPos); + System.out.println("Fitness: " + chromosome.getFitness()); + System.out.println(); + count++; + } + } + + public void mutateChromosome(Chromosome chromosome, double mutationRate) { + int[][] sudoku = chromosome.getSudoku(); + List mutablePositions = chromosome.getMutablePositions(); + + // Mutate each mutable position with a probability defined by mutationRate + for (int[] pos : mutablePositions) { + if (random.nextDouble() < mutationRate) { + int row = pos[0]; + int col = pos[1]; + int newValue = random.nextInt(9) + 1; // Assign a new value between 1 and 9 + sudoku[row][col] = newValue; + } + } + + // Update the chromosome's Sudoku and recalculate its fitness + chromosome.setSudoku(sudoku); + chromosome.evaluateFitness(); + } + + public void evaluatePopulation() { + // Evaluate the fitness of each chromosome in the population + for (Chromosome chromosome : population) { + chromosome.evaluateFitness(); + } + } + + public Chromosome getBestChromosome() { + // Find and return the chromosome with the best (lowest) fitness in the population + Chromosome best = population.get(0); + for (Chromosome chromosome : population) { + if (chromosome.getFitness() < best.getFitness()) { + best = chromosome; + } + } + return best; + } + + // Chromosome class representing an individual solution + public class Chromosome { + private int[][] sudoku; // Sudoku grid representing the chromosome + private List mutablePositions; // Positions that can be changed (mutable) + private int fitness; // Fitness value representing the number of conflicts + + // Constructor for initializing a Chromosome with a Sudoku grid and mutable positions + public Chromosome(int[][] sudoku, List mutablePositions) { + this.sudoku = sudoku; + this.mutablePositions = mutablePositions; + } + + public int[][] getSudoku() { + return sudoku; + } + + public void setSudoku(int[][] sudoku) { + this.sudoku = sudoku; + } + + public List getMutablePositions() { + return mutablePositions; + } + + public int getFitness() { + return fitness; + } + + // Evaluate the fitness of the Sudoku by counting the number of row, column, and subgrid violations + public void evaluateFitness() { + fitness = countRowViolations() + countColumnViolations() + countSubgridViolations(); + } + + private int countRowViolations() { + int violations = 0; + // Iterate through each row to count conflicts + for (int i = 0; i < 9; i++) { + boolean[] present = new boolean[10]; + for (int j = 0; j < 9; j++) { + int value = sudoku[i][j]; + if (value != 0) { + if (present[value]) { + violations++; // Increment violations if the value is already seen + } else { + present[value] = true; // Mark the value as seen + } + } + } + } + return violations; + } + private int countColumnViolations() { + int violations = 0; + // Loop through each column + for (int j = 0; j < 9; j++) { + boolean[] present = new boolean[10]; // Track numbers present in the column + for (int i = 0; i < 9; i++) { + int value = sudoku[i][j]; + if (value != 0) { + // If the number is already present, increment the violations count + if (present[value]) { + violations++; + } else { + // Mark the number as present + present[value] = true; + } + } + } + } + return violations; + } + + private int countSubgridViolations() { + int violations = 0; + // Loop through each 3x3 subgrid + for (int gridRow = 0; gridRow < 3; gridRow++) { + for (int gridCol = 0; gridCol < 3; gridCol++) { + boolean[] present = new boolean[10]; // Track numbers present in the subgrid + // Loop through cells in the 3x3 subgrid + for (int row = gridRow * 3; row < gridRow * 3 + 3; row++) { + for (int col = gridCol * 3; col < gridCol * 3 + 3; col++) { + int value = sudoku[row][col]; + if (value != 0) { + // If the number is already present, increment the violations count + if (present[value]) { + violations++; + } else { + // Mark the number as present + present[value] = true; + } + } + } + } + } + } + return violations; + } + + public void printChromosome(boolean printMutPos) { + // Print the Sudoku matrix + for (int[] row : sudoku) { + for (int j = 0; j < row.length; j++) { + System.out.print(row[j]); + if (j < row.length - 1) { + System.out.print(" "); + } + } + System.out.println(); + } + // If requested, print mutable positions + if (printMutPos) { + System.out.println("Mutable Positions:"); + for (int i = 0; i < mutablePositions.size(); i++) { + int[] pos = mutablePositions.get(i); + System.out.print("(" + pos[0] + ", " + pos[1] + ")"); + if (i < mutablePositions.size() - 1) { + System.out.print(" "); + } + } + System.out.println(); + } + } + } +} diff --git a/statistical/demo/src/main/java/com/example/Main.java b/statistical/demo/src/main/java/com/example/Main.java index 40dc4d8..c1d0c74 100644 --- a/statistical/demo/src/main/java/com/example/Main.java +++ b/statistical/demo/src/main/java/com/example/Main.java @@ -57,18 +57,18 @@ public class Main { // Choosing variables for different sudoku difficulties if (mutablePositions.size() < EASYTHRESHOLD) { // Easy sudoku - POPULATIONSIZE = 75; - TOURNAMENTSIZE = 4; - MUTATIONRATE = 0.04; + POPULATIONSIZE = 500000; + TOURNAMENTSIZE = 3; + MUTATIONRATE = 0.1; } else if (mutablePositions.size() < HARDTHRESHOLD) { // Hard sudoku - POPULATIONSIZE = 150; - TOURNAMENTSIZE = 5; - MUTATIONRATE = 0.055; + POPULATIONSIZE = 100000; + TOURNAMENTSIZE = 8; + MUTATIONRATE = 0.34; } else { // Ultra-hard sudoku - POPULATIONSIZE = 20000; - TOURNAMENTSIZE = 4; - MUTATIONRATE = 0.032; + POPULATIONSIZE = 500000; + TOURNAMENTSIZE = 3; + MUTATIONRATE = 0.1; } // Generate initial population of chromosomes @@ -109,7 +109,7 @@ public class Main { generation++; // Plot the fitness graph every 10 generations - if (generation % 100 == 0) { + if (generation % 1 == 0) { mainInstance.plotFitness(fitnessValues); } @@ -126,11 +126,32 @@ public class Main { for (int i = 0; i < numberOfChromosomes; i++) { // Create a copy of the base Sudoku int[][] sudoku = copyMatrix(baseSudoku); - // Randomly fill mutable positions - for (int[] pos : mutablePositions) { - int row = pos[0]; - int col = pos[1]; - sudoku[row][col] = random.nextInt(9) + 1; + // Randomly fill mutable positions while ensuring no duplicates in subgrids + for (int gridRow = 0; gridRow < 3; gridRow++) { + for (int gridCol = 0; gridCol < 3; gridCol++) { + boolean[] present = new boolean[10]; + List subgridPositions = new ArrayList<>(); + // Collect all positions in the current 3x3 subgrid + for (int row = gridRow * 3; row < gridRow * 3 + 3; row++) { + for (int col = gridCol * 3; col < gridCol * 3 + 3; col++) { + int value = sudoku[row][col]; + if (value != 0) { + present[value] = true; + } else { + subgridPositions.add(new int[]{row, col}); + } + } + } + // Randomly fill the subgrid ensuring no duplicates + for (int[] pos : subgridPositions) { + int newValue; + do { + newValue = random.nextInt(9) + 1; + } while (present[newValue]); + sudoku[pos[0]][pos[1]] = newValue; + present[newValue] = true; + } + } } // Create a new chromosome with the generated Sudoku and mutable positions Chromosome chromosome = new Chromosome(sudoku, new ArrayList<>(mutablePositions)); @@ -220,15 +241,29 @@ public class Main { public void mutateChromosome(Chromosome chromosome, double mutationRate) { int[][] sudoku = chromosome.getSudoku(); - List mutablePositions = chromosome.getMutablePositions(); - // Mutate each mutable position with a probability defined by mutationRate - for (int[] pos : mutablePositions) { - if (random.nextDouble() < mutationRate) { - int row = pos[0]; - int col = pos[1]; - int newValue = random.nextInt(9) + 1; // Assign a new value between 1 and 9 - sudoku[row][col] = newValue; + // With a probability defined by mutationRate, perform a mutation by swapping subgrids + if (random.nextDouble() < mutationRate) { + // Randomly select two different subgrids to swap + int subgrid1, subgrid2; + do { + subgrid1 = random.nextInt(9); + subgrid2 = random.nextInt(9); + } while (subgrid1 == subgrid2); + + // Get the starting coordinates for both subgrids + int rowStart1 = (subgrid1 / 3) * 3; + int colStart1 = (subgrid1 % 3) * 3; + int rowStart2 = (subgrid2 / 3) * 3; + int colStart2 = (subgrid2 % 3) * 3; + + // Swap the values in the two selected subgrids + for (int rowOffset = 0; rowOffset < 3; rowOffset++) { + for (int colOffset = 0; colOffset < 3; colOffset++) { + int temp = sudoku[rowStart1 + rowOffset][colStart1 + colOffset]; + sudoku[rowStart1 + rowOffset][colStart1 + colOffset] = sudoku[rowStart2 + rowOffset][colStart2 + colOffset]; + sudoku[rowStart2 + rowOffset][colStart2 + colOffset] = temp; + } } } @@ -236,6 +271,7 @@ public class Main { chromosome.setSudoku(sudoku); chromosome.evaluateFitness(); } + public void evaluatePopulation() { // Evaluate the fitness of each chromosome in the population diff --git a/statistical/demo/target/classes/com/example/Main$Chromosome.class b/statistical/demo/target/classes/com/example/Main$Chromosome.class index ad09172..815de1c 100644 Binary files a/statistical/demo/target/classes/com/example/Main$Chromosome.class and b/statistical/demo/target/classes/com/example/Main$Chromosome.class differ diff --git a/statistical/demo/target/classes/com/example/Main.class b/statistical/demo/target/classes/com/example/Main.class index b63c52e..9d0c4b8 100644 Binary files a/statistical/demo/target/classes/com/example/Main.class and b/statistical/demo/target/classes/com/example/Main.class differ