diff --git a/Main.java b/Main.java index e1c38d8..db30672 100644 --- a/Main.java +++ b/Main.java @@ -49,13 +49,13 @@ public class Main { // Choosing variables for different sudoku difficulties if (mutablePositions.size() < EASYTHRESHOLD) { // Easy sudoku - POPULATIONSIZE = 75; - TOURNAMENTSIZE = 4; - MUTATIONRATE = 0.04; - } else if (mutablePositions.size() < HARDTHRESHOLD) { // Hard sudoku - POPULATIONSIZE = 150; + POPULATIONSIZE = 100; TOURNAMENTSIZE = 5; - MUTATIONRATE = 0.055; + MUTATIONRATE = 0.05; + } else if (mutablePositions.size() < HARDTHRESHOLD) { // Hard sudoku + POPULATIONSIZE = 100; + TOURNAMENTSIZE = 5; + MUTATIONRATE = 0.057; } else { // Ultra-hard sudoku POPULATIONSIZE = 2000; diff --git a/statistical/demo/pom.xml b/statistical/demo/pom.xml new file mode 100644 index 0000000..fcce154 --- /dev/null +++ b/statistical/demo/pom.xml @@ -0,0 +1,21 @@ + + + 4.0.0 + + com.example + SudokuSolver + 1.0-SNAPSHOT + + + com.example + demo + 1.0-SNAPSHOT + + + 17 + 17 + + + \ No newline at end of file diff --git a/statistical/demo/src/main/java/com/example/Main.java b/statistical/demo/src/main/java/com/example/Main.java new file mode 100644 index 0000000..528516d --- /dev/null +++ b/statistical/demo/src/main/java/com/example/Main.java @@ -0,0 +1,382 @@ +package com.example; +import java.io.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; +import org.knowm.xchart.SwingWrapper; +import org.knowm.xchart.XYChart; +import org.knowm.xchart.XYSeries; +import org.knowm.xchart.XYSeries.XYSeriesRenderStyle; + +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 = 75; + TOURNAMENTSIZE = 4; + MUTATIONRATE = 0.04; + } else if (mutablePositions.size() < HARDTHRESHOLD) { // Hard sudoku + POPULATIONSIZE = 150; + TOURNAMENTSIZE = 5; + MUTATIONRATE = 0.055; + } + else { // Ultra-hard sudoku + POPULATIONSIZE = 2000; + TOURNAMENTSIZE = 10; + MUTATIONRATE = 0.2; + } + + // Generate initial population of chromosomes + mainInstance.generateInitialChromosomes(POPULATIONSIZE, baseSudoku, mutablePositions); + + Chromosome bestSolution = null; + List fitnessValues = new ArrayList<>(); + + int generation = 0; // Track the number of generations + while (true) { + // Evaluate the fitness of each chromosome in the population + mainInstance.evaluatePopulation(); + + // Track the best fitness value in the current generation + bestSolution = mainInstance.getBestChromosome(); + fitnessValues.add(bestSolution.getFitness()); + + 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; + + generation++; + + // Plot the fitness graph every 10 generations + if (generation % 10 == 0) { + mainInstance.plotFitness(fitnessValues); + } + + // 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 : row) { + System.out.print(j + " "); + } + System.out.println(); + } + // If requested, print mutable positions + if (printMutPos) { + System.out.println("Mutable Positions:"); + for (int[] pos : mutablePositions) { + System.out.print("(" + pos[0] + ", " + pos[1] + ") "); + } + System.out.println(); + } + } + } + + // Method to plot the fitness values over generations + public void plotFitness(List fitnessValues) { + XYChart chart = new XYChart(800, 600); + chart.setTitle("Fitness over Generations"); + chart.setXAxisTitle("Generation"); + chart.setYAxisTitle("Fitness"); + XYSeries series = chart.addSeries("Fitness", null, fitnessValues); + series.setXYSeriesRenderStyle(XYSeriesRenderStyle.Line); + new SwingWrapper<>(chart).displayChart(); + } +} diff --git a/statistical/demo/target/classes/Main$Chromosome.class b/statistical/demo/target/classes/Main$Chromosome.class new file mode 100644 index 0000000..77b54fe Binary files /dev/null and b/statistical/demo/target/classes/Main$Chromosome.class differ diff --git a/statistical/demo/target/classes/Main.class b/statistical/demo/target/classes/Main.class new file mode 100644 index 0000000..257a34b Binary files /dev/null and b/statistical/demo/target/classes/Main.class differ diff --git a/statistical/demo/target/classes/com/example/Main$Chromosome.class b/statistical/demo/target/classes/com/example/Main$Chromosome.class new file mode 100644 index 0000000..cf9d0a4 Binary files /dev/null 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 new file mode 100644 index 0000000..d7163d2 Binary files /dev/null and b/statistical/demo/target/classes/com/example/Main.class differ diff --git a/statistical/demo/target/demo-1.0-SNAPSHOT.jar b/statistical/demo/target/demo-1.0-SNAPSHOT.jar new file mode 100644 index 0000000..536580e Binary files /dev/null and b/statistical/demo/target/demo-1.0-SNAPSHOT.jar differ diff --git a/statistical/demo/target/maven-archiver/pom.properties b/statistical/demo/target/maven-archiver/pom.properties new file mode 100644 index 0000000..c822858 --- /dev/null +++ b/statistical/demo/target/maven-archiver/pom.properties @@ -0,0 +1,3 @@ +artifactId=demo +groupId=com.example +version=1.0-SNAPSHOT diff --git a/statistical/demo/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/statistical/demo/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..0a08534 --- /dev/null +++ b/statistical/demo/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,2 @@ +Main$Chromosome.class +Main.class diff --git a/statistical/demo/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/statistical/demo/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..b65d5a6 --- /dev/null +++ b/statistical/demo/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1 @@ +/home/emil/Coding/Assignments/ITAI/Sudoku_solver/statistical/demo/src/main/java/com/example/Main.java diff --git a/statistical/demo/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst b/statistical/demo/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/createdFiles.lst new file mode 100644 index 0000000..e69de29 diff --git a/statistical/demo/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst b/statistical/demo/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst new file mode 100644 index 0000000..e69de29