Compare commits
10
Commits
a28b5d6620
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aaa4a2a490 | ||
|
|
1562f775af | ||
|
|
26d454c976 | ||
|
|
39736e90fd | ||
|
|
b1615d03b1 | ||
|
|
b3ec87c1c3 | ||
|
|
681cd729bb | ||
|
|
65b3f31ba6 | ||
|
|
c1939b27ac | ||
|
|
5ff0c89688 |
@@ -1,127 +1,155 @@
|
|||||||
import java.io.*;
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
public class Main {
|
public class Main {
|
||||||
List<Chromosome> population = new ArrayList<>(); // Список хромосом
|
// 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<Chromosome> population = new ArrayList<>();
|
||||||
Random random = new Random();
|
Random random = new Random();
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
// Создаем экземпляр класса Main
|
// Base Sudoku matrix (input matrix)
|
||||||
Main mainInstance = new Main();
|
int[][] baseSudoku = new int[9][9];
|
||||||
mainInstance.generateInitialChromosomes(100); // Генерация 100 хромосом
|
// List to track positions in Sudoku that are mutable
|
||||||
|
List<int[]> 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 = 100000;
|
||||||
|
TOURNAMENTSIZE = 8;
|
||||||
|
MUTATIONRATE = 0.34;
|
||||||
|
} else if (mutablePositions.size() < HARDTHRESHOLD) { // Hard sudoku
|
||||||
|
POPULATIONSIZE = 100000;
|
||||||
|
TOURNAMENTSIZE = 8;
|
||||||
|
MUTATIONRATE = 0.34;
|
||||||
|
}
|
||||||
|
else { // Ultra-hard sudoku
|
||||||
|
POPULATIONSIZE = 500000;
|
||||||
|
TOURNAMENTSIZE = 3;
|
||||||
|
MUTATIONRATE = 0.15;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Generate initial population of 100 chromosomes
|
||||||
|
mainInstance.generateInitialChromosomes(POPULATIONSIZE, baseSudoku, mutablePositions);
|
||||||
|
|
||||||
// Убираем ограничение на количество поколений
|
|
||||||
double mutationRate = 0.05; // Вероятность мутации
|
|
||||||
Chromosome bestSolution = null;
|
Chromosome bestSolution = null;
|
||||||
|
|
||||||
int generation = 0;
|
int generation = 0; // Track the number of generations
|
||||||
while (true) {
|
while (true) {
|
||||||
// Оценка текущего поколения
|
// Evaluate the fitness of each chromosome in the population
|
||||||
mainInstance.evaluatePopulation();
|
mainInstance.evaluatePopulation();
|
||||||
|
|
||||||
// Отбор родителей и создание новых потомков
|
|
||||||
List<Chromosome> newPopulation = new ArrayList<>();
|
List<Chromosome> newPopulation = new ArrayList<>();
|
||||||
for (int i = 0; i < mainInstance.population.size() / 2; i++) {
|
for (int i = 0; i < mainInstance.population.size() / 2; i++) {
|
||||||
|
// Select parents using tournament selection
|
||||||
List<Chromosome> parents = mainInstance.tournamentSelection(5);
|
List<Chromosome> 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 child1 = mainInstance.crossoverBySubgrids(parents.get(0), parents.get(1));
|
||||||
Chromosome child2 = mainInstance.crossoverBySubgrids(parents.get(1), parents.get(0));
|
Chromosome child2 = mainInstance.crossoverBySubgrids(parents.get(1), parents.get(0));
|
||||||
|
|
||||||
// Мутация потомков
|
// Apply mutation to both children
|
||||||
mainInstance.mutateChromosome(child1, mutationRate);
|
mainInstance.mutateChromosome(child1, MUTATIONRATE);
|
||||||
mainInstance.mutateChromosome(child2, mutationRate);
|
mainInstance.mutateChromosome(child2, MUTATIONRATE);
|
||||||
|
|
||||||
|
// Add both children to the new population
|
||||||
newPopulation.add(child1);
|
newPopulation.add(child1);
|
||||||
newPopulation.add(child2);
|
newPopulation.add(child2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Замена старой популяции на новую
|
// Replace the old population with the new population
|
||||||
mainInstance.population = newPopulation;
|
mainInstance.population = newPopulation;
|
||||||
|
|
||||||
// Логирование лучшего решения текущего поколения
|
// Get the best chromosome from the current population
|
||||||
bestSolution = mainInstance.getBestChromosome();
|
bestSolution = mainInstance.getBestChromosome();
|
||||||
System.out.println("Generation " + generation + ": Best Fitness = " + bestSolution.getFitness());
|
|
||||||
generation++;
|
generation++;
|
||||||
|
|
||||||
// Критерий завершения (если найдено идеальное решение)
|
// If the best solution found has a fitness of 0, print it and end the program
|
||||||
if (bestSolution.getFitness() == 0) {
|
if (bestSolution.getFitness() == 0) {
|
||||||
System.out.println("Optimal solution found in generation " + generation);
|
bestSolution.printChromosome(false);
|
||||||
break;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Вывод финального решения
|
|
||||||
if (bestSolution != null) {
|
|
||||||
System.out.println("Final Solution:");
|
|
||||||
bestSolution.printChromosome(false);
|
|
||||||
System.out.println("Fitness: " + bestSolution.getFitness());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Пример использования турнирной селекции
|
|
||||||
List<Chromosome> parents = mainInstance.tournamentSelection(5);
|
|
||||||
System.out.println("Selected Parents:");
|
|
||||||
for (Chromosome parent : parents) {
|
|
||||||
parent.printChromosome(false);
|
|
||||||
System.out.println("Fitness: " + parent.getFitness());
|
|
||||||
}
|
|
||||||
// Пример использования кроссовера по подрешеткам
|
|
||||||
Chromosome child = mainInstance.crossoverBySubgrids(parents.get(0), parents.get(1));
|
|
||||||
// Пример мутации
|
|
||||||
mainInstance.mutateChromosome(child, 0.05); // Мутация с вероятностью 5%
|
|
||||||
System.out.println("Child Chromosome:");
|
|
||||||
child.printChromosome(false);
|
|
||||||
System.out.println("Fitness: " + child.getFitness());
|
|
||||||
System.out.println("Fitness: " + child.getFitness());
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Генерация начальной популяции хромосом
|
// Generate initial population of chromosomes
|
||||||
public void generateInitialChromosomes(int numberOfChromosomes) {
|
public void generateInitialChromosomes(int numberOfChromosomes, int[][] baseSudoku, List<int[]> mutablePositions) {
|
||||||
String inputFileName = "input.txt";
|
for (int i = 0; i < numberOfChromosomes; i++) {
|
||||||
|
// Create a copy of the base Sudoku
|
||||||
try (BufferedReader br = new BufferedReader(new FileReader(inputFileName))) {
|
int[][] sudoku = copyMatrix(baseSudoku);
|
||||||
String line;
|
// Randomly fill mutable positions while ensuring no duplicates in subgrids
|
||||||
int[][] baseSudoku = new int[9][9]; // Матрица Судоку для шаблона
|
for (int gridRow = 0; gridRow < 3; gridRow++) {
|
||||||
List<int[]> mutablePositions = new ArrayList<>(); // Список координат изменяемых позиций
|
for (int gridCol = 0; gridCol < 3; gridCol++) {
|
||||||
|
boolean[] present = new boolean[10];
|
||||||
int rowIndex = 0;
|
List<int[]> subgridPositions = new ArrayList<>();
|
||||||
while ((line = br.readLine()) != null) {
|
// Collect all positions in the current 3x3 subgrid
|
||||||
String[] tokens = line.split(" "); // Разделяем строку по пробелам
|
for (int row = gridRow * 3; row < gridRow * 3 + 3; row++) {
|
||||||
for (int colIndex = 0; colIndex < tokens.length; colIndex++) {
|
for (int col = gridCol * 3; col < gridCol * 3 + 3; col++) {
|
||||||
if (tokens[colIndex].equals("-")) {
|
int value = sudoku[row][col];
|
||||||
baseSudoku[rowIndex][colIndex] = 0; // Пустые ячейки заполняются 0
|
if (value != 0) {
|
||||||
mutablePositions.add(new int[]{rowIndex, colIndex}); // Добавляем координаты изменяемой позиции
|
present[value] = true;
|
||||||
} else {
|
} else {
|
||||||
baseSudoku[rowIndex][colIndex] = Integer.parseInt(tokens[colIndex]); // Конвертируем строку в число
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rowIndex++;
|
|
||||||
}
|
}
|
||||||
|
// Create a new chromosome with the generated Sudoku and mutable positions
|
||||||
// Создаем заданное количество хромосом
|
Chromosome chromosome = new Chromosome(sudoku, new ArrayList<>(mutablePositions));
|
||||||
for (int i = 0; i < numberOfChromosomes; i++) {
|
chromosome.evaluateFitness(); // Evaluate its fitness
|
||||||
int[][] sudoku = copyMatrix(baseSudoku); // Копируем базовый Судоку
|
population.add(chromosome); // Add to the population
|
||||||
// Заполняем изменяемые позиции случайными числами (1-9)
|
|
||||||
for (int[] pos : mutablePositions) {
|
|
||||||
int row = pos[0];
|
|
||||||
int col = pos[1];
|
|
||||||
sudoku[row][col] = random.nextInt(9) + 1;
|
|
||||||
}
|
|
||||||
// Добавляем хромосому в популяцию
|
|
||||||
Chromosome chromosome = new Chromosome(sudoku, new ArrayList<>(mutablePositions));
|
|
||||||
chromosome.evaluateFitness(); // Оценка фитнеса хромосомы
|
|
||||||
population.add(chromosome);
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
|
||||||
System.err.println("Error reading the file: " + e.getMessage());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Копирование матрицы
|
|
||||||
|
// Create a deep copy of a matrix
|
||||||
private int[][] copyMatrix(int[][] original) {
|
private int[][] copyMatrix(int[][] original) {
|
||||||
int[][] copy = new int[original.length][original[0].length];
|
int[][] copy = new int[original.length][original[0].length];
|
||||||
for (int i = 0; i < original.length; i++) {
|
for (int i = 0; i < original.length; i++) {
|
||||||
@@ -130,37 +158,39 @@ public class Main {
|
|||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Турнирная селекция
|
|
||||||
public List<Chromosome> tournamentSelection(int tournamentSize) {
|
public List<Chromosome> tournamentSelection(int tournamentSize) {
|
||||||
List<Chromosome> selectedParents = new ArrayList<>();
|
List<Chromosome> selectedParents = new ArrayList<>();
|
||||||
for (int i = 0; i < 2; i++) { // Выбираем двух родителей
|
for (int i = 0; i < 2; i++) {
|
||||||
List<Chromosome> tournament = new ArrayList<>();
|
List<Chromosome> tournament = new ArrayList<>();
|
||||||
|
// Randomly select chromosomes for the tournament
|
||||||
for (int j = 0; j < tournamentSize; j++) {
|
for (int j = 0; j < tournamentSize; j++) {
|
||||||
Chromosome randomChromosome = population.get(random.nextInt(population.size()));
|
Chromosome randomChromosome = population.get(random.nextInt(population.size()));
|
||||||
tournament.add(randomChromosome);
|
tournament.add(randomChromosome);
|
||||||
}
|
}
|
||||||
|
// Determine the best chromosome in the tournament based on fitness
|
||||||
Chromosome best = tournament.get(0);
|
Chromosome best = tournament.get(0);
|
||||||
for (Chromosome chromosome : tournament) {
|
for (Chromosome chromosome : tournament) {
|
||||||
if (chromosome.getFitness() < best.getFitness()) {
|
if (chromosome.getFitness() < best.getFitness()) {
|
||||||
best = chromosome;
|
best = chromosome;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Add the best chromosome to the list of selected parents
|
||||||
selectedParents.add(best);
|
selectedParents.add(best);
|
||||||
}
|
}
|
||||||
return selectedParents;
|
return selectedParents;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Кроссовер по подрешеткам
|
|
||||||
public Chromosome crossoverBySubgrids(Chromosome parent1, Chromosome parent2) {
|
public Chromosome crossoverBySubgrids(Chromosome parent1, Chromosome parent2) {
|
||||||
int[][] childSudoku = new int[9][9];
|
int[][] childSudoku = new int[9][9];
|
||||||
|
|
||||||
// Сначала копируем матрицу от первого родителя
|
// Copy the entire Sudoku grid from parent1 to the child
|
||||||
for (int row = 0; row < 9; row++) {
|
for (int row = 0; row < 9; row++) {
|
||||||
System.arraycopy(parent1.getSudoku()[row], 0, childSudoku[row], 0, 9);
|
System.arraycopy(parent1.getSudoku()[row], 0, childSudoku[row], 0, 9);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Выбираем случайные подрешётки для обмена (например, 4 подрешётка)
|
// Determine the number of subgrids to swap from parent2 to child (1-5)
|
||||||
int numSubgridsToSwap = random.nextInt(5) + 1; // Выбираем от 1 до 5 подрешеток для обмена
|
int numSubgridsToSwap = random.nextInt(5) + 1;
|
||||||
List<Integer> selectedSubgrids = new ArrayList<>();
|
List<Integer> selectedSubgrids = new ArrayList<>();
|
||||||
while (selectedSubgrids.size() < numSubgridsToSwap) {
|
while (selectedSubgrids.size() < numSubgridsToSwap) {
|
||||||
int subgridIndex = random.nextInt(9);
|
int subgridIndex = random.nextInt(9);
|
||||||
@@ -168,8 +198,8 @@ public class Main {
|
|||||||
selectedSubgrids.add(subgridIndex);
|
selectedSubgrids.add(subgridIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Выполняем обмен выбранных подрешётков
|
// Swap the selected subgrids from parent2 into the child
|
||||||
for (int subgrid : selectedSubgrids) {
|
for (int subgrid : selectedSubgrids) {
|
||||||
int rowStart = (subgrid / 3) * 3;
|
int rowStart = (subgrid / 3) * 3;
|
||||||
int colStart = (subgrid % 3) * 3;
|
int colStart = (subgrid % 3) * 3;
|
||||||
@@ -179,19 +209,17 @@ public class Main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Создаем и возвращаем нового потомка
|
// Create a new chromosome with the resulting child Sudoku and evaluate its fitness
|
||||||
Chromosome child = new Chromosome(childSudoku, parent1.getMutablePositions());
|
Chromosome child = new Chromosome(childSudoku, parent1.getMutablePositions());
|
||||||
child.evaluateFitness();
|
child.evaluateFitness();
|
||||||
child.evaluateFitness();
|
|
||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Вывод содержимого популяции для проверки
|
|
||||||
public void printPopulation(boolean printMutPos) {
|
public void printPopulation(boolean printMutPos) {
|
||||||
System.out.println("Generated Population:");
|
System.out.println("Generated Population:");
|
||||||
int count = 1;
|
int count = 1;
|
||||||
|
// Print each chromosome's Sudoku and fitness value
|
||||||
for (Chromosome chromosome : population) {
|
for (Chromosome chromosome : population) {
|
||||||
System.out.println("Chromosome " + count + ":");
|
System.out.println("Chromosome " + count + ":");
|
||||||
chromosome.printChromosome(printMutPos);
|
chromosome.printChromosome(printMutPos);
|
||||||
@@ -200,36 +228,50 @@ public class Main {
|
|||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Мутация хромосомы
|
|
||||||
public void mutateChromosome(Chromosome chromosome, double mutationRate) {
|
public void mutateChromosome(Chromosome chromosome, double mutationRate) {
|
||||||
int[][] sudoku = chromosome.getSudoku();
|
int[][] sudoku = chromosome.getSudoku();
|
||||||
List<int[]> mutablePositions = chromosome.getMutablePositions();
|
|
||||||
|
// With a probability defined by mutationRate, perform a mutation by swapping subgrids
|
||||||
// Проходим по всем изменяемым позициям
|
if (random.nextDouble() < mutationRate) {
|
||||||
for (int[] pos : mutablePositions) {
|
// Randomly select two different subgrids to swap
|
||||||
if (random.nextDouble() < mutationRate) {
|
int subgrid1, subgrid2;
|
||||||
int row = pos[0];
|
do {
|
||||||
int col = pos[1];
|
subgrid1 = random.nextInt(9);
|
||||||
int newValue = random.nextInt(9) + 1; // Генерируем новое значение от 1 до 9
|
subgrid2 = random.nextInt(9);
|
||||||
sudoku[row][col] = newValue; // Изменяем значение в выбранной позиции
|
} 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Обновляем матрицу и оцениваем фитнес
|
// Update the chromosome's Sudoku and recalculate its fitness
|
||||||
chromosome.setSudoku(sudoku);
|
chromosome.setSudoku(sudoku);
|
||||||
chromosome.evaluateFitness();
|
chromosome.evaluateFitness();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Метод для оценки всей популяции
|
|
||||||
public void evaluatePopulation() {
|
public void evaluatePopulation() {
|
||||||
|
// Evaluate the fitness of each chromosome in the population
|
||||||
for (Chromosome chromosome : population) {
|
for (Chromosome chromosome : population) {
|
||||||
chromosome.evaluateFitness();
|
chromosome.evaluateFitness();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Метод для получения лучшей хромосомы в популяции
|
|
||||||
public Chromosome getBestChromosome() {
|
public Chromosome getBestChromosome() {
|
||||||
|
// Find and return the chromosome with the best (lowest) fitness in the population
|
||||||
Chromosome best = population.get(0);
|
Chromosome best = population.get(0);
|
||||||
for (Chromosome chromosome : population) {
|
for (Chromosome chromosome : population) {
|
||||||
if (chromosome.getFitness() < best.getFitness()) {
|
if (chromosome.getFitness() < best.getFitness()) {
|
||||||
@@ -238,100 +280,96 @@ public class Main {
|
|||||||
}
|
}
|
||||||
return best;
|
return best;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Класс хромосомы
|
// Chromosome class representing an individual solution
|
||||||
public class Chromosome {
|
public class Chromosome {
|
||||||
private int[][] sudoku; // Матрица Судоку
|
private int[][] sudoku; // Sudoku grid representing the chromosome
|
||||||
private List<int[]> mutablePositions; // Список координат изменяемых позиций
|
private List<int[]> mutablePositions; // Positions that can be changed (mutable)
|
||||||
private int fitness; // Значение фитнеса
|
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<int[]> mutablePositions) {
|
public Chromosome(int[][] sudoku, List<int[]> mutablePositions) {
|
||||||
this.sudoku = sudoku;
|
this.sudoku = sudoku;
|
||||||
this.mutablePositions = mutablePositions;
|
this.mutablePositions = mutablePositions;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Геттер для матрицы Судоку
|
|
||||||
public int[][] getSudoku() {
|
public int[][] getSudoku() {
|
||||||
return sudoku;
|
return sudoku;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Сеттер для матрицы Судоку
|
|
||||||
public void setSudoku(int[][] sudoku) {
|
public void setSudoku(int[][] sudoku) {
|
||||||
this.sudoku = sudoku;
|
this.sudoku = sudoku;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Геттер для изменяемых позиций
|
|
||||||
public List<int[]> getMutablePositions() {
|
public List<int[]> getMutablePositions() {
|
||||||
return mutablePositions;
|
return mutablePositions;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Сеттер для изменяемых позиций
|
|
||||||
public void setMutablePositions(List<int[]> mutablePositions) {
|
|
||||||
this.mutablePositions = mutablePositions;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Геттер для фитнеса
|
|
||||||
public int getFitness() {
|
public int getFitness() {
|
||||||
return fitness;
|
return fitness;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Метод для оценки фитнеса хромосомы
|
// Evaluate the fitness of the Sudoku by counting the number of row, column, and subgrid violations
|
||||||
public void evaluateFitness() {
|
public void evaluateFitness() {
|
||||||
fitness = countRowViolations() + countColumnViolations() + countSubgridViolations();
|
fitness = countRowViolations() + countColumnViolations();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Подсчет нарушений в строках
|
|
||||||
private int countRowViolations() {
|
private int countRowViolations() {
|
||||||
int violations = 0;
|
int violations = 0;
|
||||||
for (int[] row : sudoku) {
|
// Iterate through each row to count conflicts
|
||||||
boolean[] seen = new boolean[10];
|
for (int i = 0; i < 9; i++) {
|
||||||
for (int num : row) {
|
boolean[] present = new boolean[10];
|
||||||
if (num != 0) {
|
for (int j = 0; j < 9; j++) {
|
||||||
if (seen[num]) {
|
int value = sudoku[i][j];
|
||||||
violations++;
|
if (value != 0) {
|
||||||
|
if (present[value]) {
|
||||||
|
violations++; // Increment violations if the value is already seen
|
||||||
} else {
|
} else {
|
||||||
seen[num] = true;
|
present[value] = true; // Mark the value as seen
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return violations;
|
return violations;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Подсчет нарушений в столбцах
|
|
||||||
private int countColumnViolations() {
|
private int countColumnViolations() {
|
||||||
int violations = 0;
|
int violations = 0;
|
||||||
for (int col = 0; col < 9; col++) {
|
// Loop through each column
|
||||||
boolean[] seen = new boolean[10];
|
for (int j = 0; j < 9; j++) {
|
||||||
for (int row = 0; row < 9; row++) {
|
boolean[] present = new boolean[10]; // Track numbers present in the column
|
||||||
int num = sudoku[row][col];
|
for (int i = 0; i < 9; i++) {
|
||||||
if (num != 0) {
|
int value = sudoku[i][j];
|
||||||
if (seen[num]) {
|
if (value != 0) {
|
||||||
|
// If the number is already present, increment the violations count
|
||||||
|
if (present[value]) {
|
||||||
violations++;
|
violations++;
|
||||||
} else {
|
} else {
|
||||||
seen[num] = true;
|
// Mark the number as present
|
||||||
|
present[value] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return violations;
|
return violations;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Подсчет нарушений в подрешетках 3x3
|
|
||||||
private int countSubgridViolations() {
|
private int countSubgridViolations() {
|
||||||
int violations = 0;
|
int violations = 0;
|
||||||
for (int rowStart = 0; rowStart < 9; rowStart += 3) {
|
// Loop through each 3x3 subgrid
|
||||||
for (int colStart = 0; colStart < 9; colStart += 3) {
|
for (int gridRow = 0; gridRow < 3; gridRow++) {
|
||||||
boolean[] seen = new boolean[10];
|
for (int gridCol = 0; gridCol < 3; gridCol++) {
|
||||||
for (int row = rowStart; row < rowStart + 3; row++) {
|
boolean[] present = new boolean[10]; // Track numbers present in the subgrid
|
||||||
for (int col = colStart; col < colStart + 3; col++) {
|
// Loop through cells in the 3x3 subgrid
|
||||||
int num = sudoku[row][col];
|
for (int row = gridRow * 3; row < gridRow * 3 + 3; row++) {
|
||||||
if (num != 0) {
|
for (int col = gridCol * 3; col < gridCol * 3 + 3; col++) {
|
||||||
if (seen[num]) {
|
int value = sudoku[row][col];
|
||||||
|
if (value != 0) {
|
||||||
|
// If the number is already present, increment the violations count
|
||||||
|
if (present[value]) {
|
||||||
violations++;
|
violations++;
|
||||||
} else {
|
} else {
|
||||||
seen[num] = true;
|
// Mark the number as present
|
||||||
|
present[value] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -340,22 +378,30 @@ public class Main {
|
|||||||
}
|
}
|
||||||
return violations;
|
return violations;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Метод для отображения состояния хромосомы
|
|
||||||
public void printChromosome(boolean printMutPos) {
|
public void printChromosome(boolean printMutPos) {
|
||||||
System.out.println("Sudoku Matrix:");
|
// Print the Sudoku matrix
|
||||||
for (int[] row : sudoku) {
|
for (int[] row : sudoku) {
|
||||||
for (int num : row) {
|
for (int j = 0; j < row.length; j++) {
|
||||||
System.out.print(num + " ");
|
System.out.print(row[j]);
|
||||||
|
if (j < row.length - 1) {
|
||||||
|
System.out.print(" ");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
System.out.println();
|
System.out.println();
|
||||||
}
|
}
|
||||||
|
// If requested, print mutable positions
|
||||||
if (printMutPos) {
|
if (printMutPos) {
|
||||||
System.out.println("Mutable Positions (Coordinates):");
|
System.out.println("Mutable Positions:");
|
||||||
for (int[] pos : mutablePositions) {
|
for (int i = 0; i < mutablePositions.size(); i++) {
|
||||||
System.out.println("Row: " + pos[0] + ", Col: " + pos[1]);
|
int[] pos = mutablePositions.get(i);
|
||||||
|
System.out.print("(" + pos[0] + ", " + pos[1] + ")");
|
||||||
|
if (i < mutablePositions.size() - 1) {
|
||||||
|
System.out.print(" ");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
System.out.println();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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<Chromosome> 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<int[]> 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<Chromosome> newPopulation = new ArrayList<>();
|
||||||
|
for (int i = 0; i < mainInstance.population.size() / 2; i++) {
|
||||||
|
// Select parents using tournament selection
|
||||||
|
List<Chromosome> 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<int[]> 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<Chromosome> tournamentSelection(int tournamentSize) {
|
||||||
|
List<Chromosome> selectedParents = new ArrayList<>();
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
List<Chromosome> 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<Integer> 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<int[]> 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<int[]> 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<int[]> mutablePositions) {
|
||||||
|
this.sudoku = sudoku;
|
||||||
|
this.mutablePositions = mutablePositions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[][] getSudoku() {
|
||||||
|
return sudoku;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSudoku(int[][] sudoku) {
|
||||||
|
this.sudoku = sudoku;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<int[]> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,115 @@
|
|||||||
|
import subprocess
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
process = subprocess.Popen(['java', './Main.java'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||||
|
|
||||||
|
info = []
|
||||||
|
for i in range(9):
|
||||||
|
info.append([""] * 9)
|
||||||
|
|
||||||
|
def check(x, y):
|
||||||
|
a = 0
|
||||||
|
res = []
|
||||||
|
if info[x][y] != "":
|
||||||
|
a += 1
|
||||||
|
res.append(info[x][y])
|
||||||
|
for dx in range(-1, 2):
|
||||||
|
for dy in range(-1, 2):
|
||||||
|
if (0 <= x+dx <= 8 and 0 <= y+dy <= 8 and (dy != 0 or dx != 0) and info[x+dx][y+dy] == 'A'):
|
||||||
|
a += 1
|
||||||
|
res.append("P")
|
||||||
|
return [a, res]
|
||||||
|
if (0 <= x-1 <= 8 and info[x-1][y] == 'S' or 0 <= x+1 <= 8 and info[x+1][y] == 'S' or 0 <= y-1 <= 8 and info[x][y-1] == 'S' or 0 <= y+1 <= 8 and info[x][y+1] == 'S'):
|
||||||
|
a += 1
|
||||||
|
res.append("P")
|
||||||
|
return [a, res]
|
||||||
|
|
||||||
|
return [a, res]
|
||||||
|
|
||||||
|
|
||||||
|
def show(neo_x, neo_y):
|
||||||
|
with open('close.txt', encoding='utf-8', mode='w') as f1:
|
||||||
|
a = list(map)
|
||||||
|
a[map.find('𖨆')] = ' '
|
||||||
|
a[map.find(str(neo_x) + " |") + 4 + neo_y * 4] = '𖨆'
|
||||||
|
f1.write(''.join(a))
|
||||||
|
|
||||||
|
map = ''
|
||||||
|
keymaker_x = 0
|
||||||
|
keymaker_y = 0
|
||||||
|
key_x = 99
|
||||||
|
key_y = 99
|
||||||
|
with open('field.txt', encoding='utf-8') as initial:
|
||||||
|
i = 1
|
||||||
|
for line in initial:
|
||||||
|
map += line
|
||||||
|
if (3 <= i and i <= 19 and i % 2 == 1):
|
||||||
|
line = line.split('|')[1:-1]
|
||||||
|
for j in range(len(line)):
|
||||||
|
if (line[j] == ' ■ '):
|
||||||
|
info[i // 2 - 1][j] = 'A'
|
||||||
|
elif (line[j] == ' □ '):
|
||||||
|
info[i // 2 - 1][j] = 'S'
|
||||||
|
elif (line[j] == ' ⚷ '):
|
||||||
|
info[i // 2 - 1][j] = 'B'
|
||||||
|
key_x = i // 2 - 1
|
||||||
|
key_y = j
|
||||||
|
elif (line[j] == ' K '):
|
||||||
|
info[i // 2 - 1][j] = 'K'
|
||||||
|
keymaker_x = i // 2 - 1
|
||||||
|
keymaker_y = j
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
# show(0, 0)
|
||||||
|
mode = 1
|
||||||
|
process.stdin.write((str(mode) + "\n" +
|
||||||
|
str(keymaker_x) + " " +
|
||||||
|
str(keymaker_y) + "\n"))
|
||||||
|
process.stdin.flush()
|
||||||
|
print(str(mode) + "\n" + str(keymaker_x) + " " + str(keymaker_y))
|
||||||
|
|
||||||
|
now_x = 0
|
||||||
|
now_y = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
move = process.stdout.readline().replace('\n', '')
|
||||||
|
print(move)
|
||||||
|
if 'e' in move:
|
||||||
|
break
|
||||||
|
|
||||||
|
move = move.split()
|
||||||
|
temp_x = int(move[1])
|
||||||
|
temp_y = int(move[2])
|
||||||
|
# show(temp_x, temp_y)
|
||||||
|
if not(temp_x + 1 == now_x and temp_y == now_y or temp_x - 1 == now_x and temp_y == now_y or temp_x == now_x and temp_y + 1 == now_y or temp_x == now_x and temp_y - 1 == now_y or temp_x == now_x and temp_y == now_y):
|
||||||
|
print("Error: Teleport")
|
||||||
|
break
|
||||||
|
now_x = int(move[1])
|
||||||
|
now_y = int(move[2])
|
||||||
|
# sleep(0.5)
|
||||||
|
|
||||||
|
res = ''
|
||||||
|
num = 0
|
||||||
|
|
||||||
|
if mode == 1:
|
||||||
|
left = -1
|
||||||
|
right = 2
|
||||||
|
else:
|
||||||
|
left = -2
|
||||||
|
right = 3
|
||||||
|
|
||||||
|
for dx in range(left, right):
|
||||||
|
for dy in range(left, right):
|
||||||
|
new_x = now_x + dx
|
||||||
|
new_y = now_y + dy
|
||||||
|
if 0 <= new_x <= 8 and 0 <= new_y <= 8:
|
||||||
|
a = check(new_x, new_y)
|
||||||
|
num += a[0]
|
||||||
|
for elem in a[1]:
|
||||||
|
res += str(new_x) + " " + str(new_y) + " " + elem + "\n"
|
||||||
|
|
||||||
|
# Кодируем результат перед записью
|
||||||
|
send = str(num) + '\n' + res
|
||||||
|
process.stdin.write(send)
|
||||||
|
print(send[:-1])
|
||||||
|
process.stdin.flush()
|
||||||
@@ -0,0 +1,180 @@
|
|||||||
|
import pandas as pd
|
||||||
|
from statistics import multimode, mean, median, stdev
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
from matplotlib.backends.backend_pdf import PdfPages
|
||||||
|
|
||||||
|
def calculate_statistics(execution_times):
|
||||||
|
"""Calculate mean, median, mode, and standard deviation."""
|
||||||
|
mean_time = mean(execution_times)
|
||||||
|
median_time = median(execution_times)
|
||||||
|
modes = multimode(execution_times)
|
||||||
|
std_dev = stdev(execution_times)
|
||||||
|
|
||||||
|
# Handle multiple modes
|
||||||
|
if len(modes) == 1:
|
||||||
|
mode_time = modes[0]
|
||||||
|
else:
|
||||||
|
mode_time = modes # List of modes
|
||||||
|
|
||||||
|
return mean_time, median_time, mode_time, std_dev
|
||||||
|
|
||||||
|
def plot_histogram(execution_times, mean_time, median_time, mode_time, std_dev):
|
||||||
|
"""Plot a histogram of execution times with mean, median, mode, and standard deviation."""
|
||||||
|
plt.figure(figsize=(10, 6))
|
||||||
|
|
||||||
|
# Define bin range to focus between 25 and 35 ms
|
||||||
|
bin_start = 25
|
||||||
|
bin_end = 35
|
||||||
|
bins = list(range(bin_start, bin_end + 1)) # Bins from 25 to 35
|
||||||
|
|
||||||
|
# Plot the main histogram
|
||||||
|
plt.hist(execution_times, bins=bins, edgecolor='black', alpha=0.7, label='Execution Times (25-35 ms)')
|
||||||
|
|
||||||
|
# Plot outliers (below 25 or above 35 ms)
|
||||||
|
outliers = [x for x in execution_times if x < bin_start or x > bin_end]
|
||||||
|
if outliers:
|
||||||
|
# Determine appropriate bins for outliers
|
||||||
|
outlier_min = min(outliers)
|
||||||
|
outlier_max = max(outliers)
|
||||||
|
outlier_bins = list(range(outlier_min, outlier_max + 2))
|
||||||
|
plt.hist(outliers, bins=outlier_bins, edgecolor='black', alpha=0.7, color='red', label='Outliers (<25 or >35 ms)')
|
||||||
|
|
||||||
|
plt.title('Histogram of Execution Times')
|
||||||
|
plt.xlabel('Execution Time (ms)')
|
||||||
|
plt.ylabel('Frequency')
|
||||||
|
|
||||||
|
# Plot mean
|
||||||
|
plt.axvline(mean_time, color='blue', linestyle='dashed', linewidth=1.5, label=f'Mean: {mean_time:.2f} ms')
|
||||||
|
|
||||||
|
# Plot median
|
||||||
|
plt.axvline(median_time, color='green', linestyle='dashed', linewidth=1.5, label=f'Median: {median_time} ms')
|
||||||
|
|
||||||
|
# Plot mode(s)
|
||||||
|
if isinstance(mode_time, list):
|
||||||
|
for m in mode_time:
|
||||||
|
plt.axvline(m, color='purple', linestyle='dashed', linewidth=1.5, label=f'Mode: {m} ms')
|
||||||
|
else:
|
||||||
|
plt.axvline(mode_time, color='purple', linestyle='dashed', linewidth=1.5, label=f'Mode: {mode_time} ms')
|
||||||
|
|
||||||
|
# Shade the area within one standard deviation from the mean
|
||||||
|
plt.axvspan(mean_time - std_dev, mean_time + std_dev, color='yellow', alpha=0.2, label='±1 Standard Deviation')
|
||||||
|
|
||||||
|
# Set x-axis limits to focus on 25-35 ms with some padding for outliers
|
||||||
|
plt.xlim(bin_start - 5, bin_end + 5) # Extending a bit to show outliers
|
||||||
|
|
||||||
|
plt.legend()
|
||||||
|
plt.tight_layout()
|
||||||
|
return plt.gcf() # Return the current figure
|
||||||
|
|
||||||
|
def plot_boxplot(execution_times):
|
||||||
|
"""Plot a box plot of execution times."""
|
||||||
|
plt.figure(figsize=(10, 6))
|
||||||
|
plt.boxplot(execution_times, vert=False, patch_artist=True, boxprops=dict(facecolor='lightblue'))
|
||||||
|
plt.title('Box Plot of Execution Times')
|
||||||
|
plt.xlabel('Execution Time (ms)')
|
||||||
|
plt.tight_layout()
|
||||||
|
return plt.gcf()
|
||||||
|
|
||||||
|
def generate_pdf_report(csv_file, output_pdf):
|
||||||
|
"""Generate a PDF report containing statistics and visualizations."""
|
||||||
|
# Read the CSV file with error handling
|
||||||
|
try:
|
||||||
|
data = pd.read_csv(csv_file)
|
||||||
|
except FileNotFoundError:
|
||||||
|
print(f"Error: The file '{csv_file}' was not found.")
|
||||||
|
return
|
||||||
|
except pd.errors.EmptyDataError:
|
||||||
|
print(f"Error: The file '{csv_file}' is empty.")
|
||||||
|
return
|
||||||
|
except pd.errors.ParserError:
|
||||||
|
print(f"Error: The file '{csv_file}' does not appear to be in CSV format.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check if 'ExecutionTime_ms' column exists
|
||||||
|
if 'ExecutionTime_ms' not in data.columns:
|
||||||
|
print("Error: 'ExecutionTime_ms' column not found in the CSV file.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Extract execution times
|
||||||
|
execution_times = data['ExecutionTime_ms'].tolist()
|
||||||
|
|
||||||
|
# Validate execution times
|
||||||
|
if not execution_times:
|
||||||
|
print("Error: No execution time data found.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check for non-numeric values
|
||||||
|
non_numeric = [x for x in execution_times if not isinstance(x, (int, float))]
|
||||||
|
if non_numeric:
|
||||||
|
print("Error: Non-numeric values found in 'ExecutionTime_ms' column.")
|
||||||
|
print(non_numeric)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Check if there are enough data points for standard deviation
|
||||||
|
if len(execution_times) < 2:
|
||||||
|
print("Error: At least two execution time data points are required to calculate standard deviation.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Calculate statistics
|
||||||
|
mean_time, median_time, mode_time, std_dev = calculate_statistics(execution_times)
|
||||||
|
|
||||||
|
# Debug print statements
|
||||||
|
print(f"Mean: {mean_time:.2f} ms")
|
||||||
|
print(f"Median: {median_time} ms")
|
||||||
|
print(f"Mode: {mode_time if isinstance(mode_time, list) else [mode_time]} ms")
|
||||||
|
print(f"Standard Deviation: {std_dev:.2f} ms")
|
||||||
|
|
||||||
|
# Create histogram plot with standard deviation shaded
|
||||||
|
fig_hist = plot_histogram(execution_times, mean_time, median_time, mode_time, std_dev)
|
||||||
|
|
||||||
|
# Create box plot
|
||||||
|
fig_box = plot_boxplot(execution_times)
|
||||||
|
|
||||||
|
# Prepare statistics text
|
||||||
|
if isinstance(mode_time, list):
|
||||||
|
mode_str = ', '.join(map(str, mode_time))
|
||||||
|
else:
|
||||||
|
mode_str = str(mode_time)
|
||||||
|
|
||||||
|
stats_text = f"""
|
||||||
|
Execution Time Statistics
|
||||||
|
=========================
|
||||||
|
|
||||||
|
Total Runs: {len(execution_times)}
|
||||||
|
|
||||||
|
Mean: {mean_time:.2f} ms
|
||||||
|
Median: {median_time} ms
|
||||||
|
Mode: {mode_str} ms
|
||||||
|
Standard Deviation: {std_dev:.2f} ms
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Create PDF
|
||||||
|
with PdfPages(output_pdf) as pdf:
|
||||||
|
# Page 1: Histogram
|
||||||
|
pdf.savefig(fig_hist)
|
||||||
|
plt.close(fig_hist)
|
||||||
|
|
||||||
|
# Page 2: Box Plot
|
||||||
|
pdf.savefig(fig_box)
|
||||||
|
plt.close(fig_box)
|
||||||
|
|
||||||
|
# Page 3: Statistics Summary
|
||||||
|
plt.figure(figsize=(8.5, 11))
|
||||||
|
plt.axis('off') # Hide axes
|
||||||
|
|
||||||
|
# Add text to the figure
|
||||||
|
plt.text(0.5, 0.5, stats_text, horizontalalignment='center', verticalalignment='center', fontsize=12, wrap=True)
|
||||||
|
|
||||||
|
# Add the statistics page to the PDF
|
||||||
|
pdf.savefig()
|
||||||
|
plt.close()
|
||||||
|
|
||||||
|
print(f"PDF report '{output_pdf}' has been generated successfully.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# Define input and output files
|
||||||
|
csv_file = 'execution_times.csv'
|
||||||
|
output_pdf = 'execution_time_report.pdf'
|
||||||
|
|
||||||
|
# Generate the PDF report
|
||||||
|
generate_pdf_report(csv_file, output_pdf)
|
||||||
@@ -0,0 +1,238 @@
|
|||||||
|
import subprocess
|
||||||
|
from random import randint
|
||||||
|
import timeit
|
||||||
|
import statistics
|
||||||
|
|
||||||
|
# astar = ['python', './astar.py']
|
||||||
|
# backtracking = ['python', './bt.exe']
|
||||||
|
astar = ['./code.exe']
|
||||||
|
backtracking = ['./bt.exe']
|
||||||
|
|
||||||
|
process1 = subprocess.Popen(astar, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||||
|
process2 = subprocess.Popen(astar, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||||
|
process3 = subprocess.Popen(backtracking, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||||
|
|
||||||
|
wins_Astar = 0
|
||||||
|
losses_Astar = 0
|
||||||
|
wins_bt = 0
|
||||||
|
losses_bt = 0
|
||||||
|
|
||||||
|
def check(x, y):
|
||||||
|
a = 0
|
||||||
|
res = []
|
||||||
|
if info[x][y] != "":
|
||||||
|
a += 1
|
||||||
|
res.append(info[x][y])
|
||||||
|
for dx in range(-1, 2):
|
||||||
|
for dy in range(-1, 2):
|
||||||
|
if (0 <= x+dx <= 8 and 0 <= y+dy <= 8 and info[x+dx][y+dy] == 'A'):
|
||||||
|
a += 1
|
||||||
|
res.append("P")
|
||||||
|
return [a, res]
|
||||||
|
if (0 <= x-1 <= 8 and info[x-1][y] == 'S' or 0 <= x+1 <= 8 and info[x+1][y] == 'S' or 0 <= y-1 <= 8 and info[x][y-1] == 'S' or 0 <= y+1 <= 8 and info[x][y+1] == 'S'):
|
||||||
|
a += 1
|
||||||
|
res.append("P")
|
||||||
|
return [a, res]
|
||||||
|
|
||||||
|
return [a, res]
|
||||||
|
|
||||||
|
def Astar_mode_1():
|
||||||
|
global info, process1, wins_Astar, losses_Astar, keymaker_x, keymaker_y, key_x, key_y
|
||||||
|
mode = 1
|
||||||
|
process1.stdin.write(str(mode) + "\n" + str(keymaker_x) + " " + str(keymaker_y) + "\n")
|
||||||
|
process1.stdin.flush()
|
||||||
|
now_x = 0
|
||||||
|
now_y = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
move = process1.stdout.readline().strip()
|
||||||
|
if 'e' in move:
|
||||||
|
if (move[-2:] == '-1'):
|
||||||
|
losses_Astar += 1
|
||||||
|
else:
|
||||||
|
wins_Astar += 1
|
||||||
|
break
|
||||||
|
move = move.split()
|
||||||
|
now_x = int(move[1])
|
||||||
|
now_y = int(move[2])
|
||||||
|
|
||||||
|
res = ''
|
||||||
|
num = 0
|
||||||
|
|
||||||
|
if mode == 1:
|
||||||
|
left = -1
|
||||||
|
right = 2
|
||||||
|
else:
|
||||||
|
left = -2
|
||||||
|
right = 3
|
||||||
|
|
||||||
|
for dx in range(left, right):
|
||||||
|
for dy in range(left, right):
|
||||||
|
new_x = now_x + dx
|
||||||
|
new_y = now_y + dy
|
||||||
|
if 0 <= new_x <= 8 and 0 <= new_y <= 8:
|
||||||
|
a = check(new_x, new_y)
|
||||||
|
num += a[0]
|
||||||
|
for elem in a[1]:
|
||||||
|
res += str(new_x) + " " + str(new_y) + " " + elem + "\n"
|
||||||
|
|
||||||
|
process1.stdin.write(str(num) + '\n' + res)
|
||||||
|
process1.stdin.flush()
|
||||||
|
|
||||||
|
def Astar_mode_2():
|
||||||
|
global info, process2, keymaker_x, keymaker_y, key_x, key_y
|
||||||
|
mode = 2
|
||||||
|
process2.stdin.write(str(mode) + "\n" + str(keymaker_x) + " " + str(keymaker_y) + "\n")
|
||||||
|
process2.stdin.flush()
|
||||||
|
now_x = 0
|
||||||
|
now_y = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
move = process2.stdout.readline().strip()
|
||||||
|
if 'e' in move:
|
||||||
|
break
|
||||||
|
move = move.split()
|
||||||
|
now_x = int(move[1])
|
||||||
|
now_y = int(move[2])
|
||||||
|
|
||||||
|
res = ''
|
||||||
|
num = 0
|
||||||
|
|
||||||
|
if mode == 1:
|
||||||
|
left = -1
|
||||||
|
right = 2
|
||||||
|
else:
|
||||||
|
left = -2
|
||||||
|
right = 3
|
||||||
|
|
||||||
|
for dx in range(left, right):
|
||||||
|
for dy in range(left, right):
|
||||||
|
new_x = now_x + dx
|
||||||
|
new_y = now_y + dy
|
||||||
|
if 0 <= new_x <= 8 and 0 <= new_y <= 8:
|
||||||
|
a = check(new_x, new_y)
|
||||||
|
num += a[0]
|
||||||
|
for elem in a[1]:
|
||||||
|
res += str(new_x) + " " + str(new_y) + " " + elem + "\n"
|
||||||
|
|
||||||
|
process2.stdin.write(str(num) + '\n' + res)
|
||||||
|
process2.stdin.flush()
|
||||||
|
|
||||||
|
def back():
|
||||||
|
global info, process3, wins_bt, losses_bt, keymaker_x, keymaker_y, key_x, key_y
|
||||||
|
mode = randint(1, 2)
|
||||||
|
process3.stdin.write(str(mode) + "\n" + str(keymaker_x) + " " + str(keymaker_y) + "\n")
|
||||||
|
process3.stdin.flush()
|
||||||
|
now_x = 0
|
||||||
|
now_y = 0
|
||||||
|
|
||||||
|
while True:
|
||||||
|
move = process3.stdout.readline().strip()
|
||||||
|
if 'e' in move:
|
||||||
|
if (move[-2:] == '-1'):
|
||||||
|
losses_bt += 1
|
||||||
|
else:
|
||||||
|
wins_bt += 1
|
||||||
|
break
|
||||||
|
move = move.split()
|
||||||
|
now_x = int(move[1])
|
||||||
|
now_y = int(move[2])
|
||||||
|
|
||||||
|
res = ''
|
||||||
|
num = 0
|
||||||
|
|
||||||
|
if mode == 1:
|
||||||
|
left = -1
|
||||||
|
right = 2
|
||||||
|
else:
|
||||||
|
left = -2
|
||||||
|
right = 3
|
||||||
|
|
||||||
|
for dx in range(left, right):
|
||||||
|
for dy in range(left, right):
|
||||||
|
new_x = now_x + dx
|
||||||
|
new_y = now_y + dy
|
||||||
|
if 0 <= new_x <= 8 and 0 <= new_y <= 8:
|
||||||
|
a = check(new_x, new_y)
|
||||||
|
num += a[0]
|
||||||
|
for elem in a[1]:
|
||||||
|
res += str(new_x) + " " + str(new_y) + " " + elem + "\n"
|
||||||
|
|
||||||
|
process3.stdin.write(str(num) + '\n' + res)
|
||||||
|
process3.stdin.flush()
|
||||||
|
|
||||||
|
def mapgen():
|
||||||
|
global info, keymaker_x, keymaker_y, key_x, key_y, process1, process2, process3, astar, backtracking
|
||||||
|
|
||||||
|
info = []
|
||||||
|
for i in range(9):
|
||||||
|
info.append([""] * 9)
|
||||||
|
info[0][0] = 'N'
|
||||||
|
keymaker_x = randint(0, 8)
|
||||||
|
keymaker_y = randint(0, 8)
|
||||||
|
while (info[keymaker_x][keymaker_y] != ''):
|
||||||
|
keymaker_x = randint(0, 8)
|
||||||
|
keymaker_y = randint(0, 8)
|
||||||
|
info[keymaker_x][keymaker_y] = 'K'
|
||||||
|
|
||||||
|
key_x = randint(0, 8)
|
||||||
|
key_y = randint(0, 8)
|
||||||
|
while (info[key_x][key_y] != ""):
|
||||||
|
key_x = randint(0, 8)
|
||||||
|
key_y = randint(0, 8)
|
||||||
|
info[key_x][key_y] = 'B'
|
||||||
|
|
||||||
|
for smith in range(randint(0, 3)):
|
||||||
|
x = randint(0, 8)
|
||||||
|
y = randint(0, 8)
|
||||||
|
while (x == 0 and y == 0 or x == 0 and y == 1 or x == 1 and y == 0 or x == 1 and y == 1 or info[x][y] != '' or (x-1 >= 0 and y-1 >=0 and (info[x-1][y-1] == 'K' or info[x-1][y-1] == "B")) or (x-1 >= 0 and (info[x-1][y] == 'K' or info[x-1][y] == 'B')) or (x-1 >= 0 and y+1 <= 8 and (info[x-1][y+1] == 'K' or info[x-1][y+1] == 'B')) or (y-1 >= 0 and (info[x][y-1] == 'K' or info[x][y-1] == 'B')) or (y+1 <= 8 and (info[x][y+1] == 'K' or info[x][y+1] == 'B')) or (x+1 <= 8 and y-1 >= 0 and (info[x+1][y-1] == 'K' or info[x+1][y-1] == 'B')) or (x+1 <= 8 and (info[x+1][y] == 'K' or info[x+1][y] == 'B')) or (x+1 <= 8 and y+1 <= 8 and (info[x+1][y+1] == 'K' or info[x+1][y+1] == 'B'))):
|
||||||
|
x = randint(0, 8)
|
||||||
|
y = randint(0, 8)
|
||||||
|
info[x][y] = 'A'
|
||||||
|
|
||||||
|
for sentiel in range(randint(0, 1)):
|
||||||
|
x = randint(0, 8)
|
||||||
|
y = randint(0, 8)
|
||||||
|
while (x == 0 and y == 0 or x == 0 and y == 1 or x == 1 and y == 0 or info[x][y] != '' or (x-1 >= 0 and (info[x-1][y] == 'K' or info[x-1][y] == 'B')) or (x+1 <= 8 and (info[x+1][y] == 'K' or info[x+1][y] == 'B')) or (y-1 >= 0 and (info[x][y-1] == 'K' or info[x][y-1] == 'B')) or (y+1 <= 8 and (info[x][y+1] == 'K' or info[x][y+1] == 'B'))):
|
||||||
|
x = randint(0, 8)
|
||||||
|
y = randint(0, 8)
|
||||||
|
info[x][y] = 'S'
|
||||||
|
|
||||||
|
info[0][0] = ''
|
||||||
|
process1 = subprocess.Popen(astar, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||||
|
process2 = subprocess.Popen(astar, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||||
|
process3 = subprocess.Popen(backtracking, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
||||||
|
|
||||||
|
execution_time_1 = []
|
||||||
|
execution_time_2 = []
|
||||||
|
execution_time_3 = []
|
||||||
|
for i in range(1, 1001):
|
||||||
|
mapgen()
|
||||||
|
execution_time_1.append(timeit.timeit(Astar_mode_1, number=1) * 1000000)
|
||||||
|
execution_time_2.append(timeit.timeit(Astar_mode_2, number=1) * 1000000)
|
||||||
|
execution_time_3.append(timeit.timeit(back, number=1) * 1000000)
|
||||||
|
if (i % 100 == 0):
|
||||||
|
print('Запущено карт', i)
|
||||||
|
|
||||||
|
print("Execution time (A* mode 1)")
|
||||||
|
print("Mean:", statistics.mean(execution_time_1))
|
||||||
|
print("Mode:", statistics.mode(execution_time_1))
|
||||||
|
print("Median:", statistics.median(execution_time_1))
|
||||||
|
print("Standart deviation:", statistics.stdev(execution_time_1))
|
||||||
|
print()
|
||||||
|
print("Execution time (A* mode 2)")
|
||||||
|
print("Mean:", statistics.mean(execution_time_2))
|
||||||
|
print("Mode:", statistics.mode(execution_time_2))
|
||||||
|
print("Median:", statistics.median(execution_time_2))
|
||||||
|
print("Standart deviation:", statistics.stdev(execution_time_2))
|
||||||
|
print()
|
||||||
|
print("Execution time (Backtrack)")
|
||||||
|
print("Mean:", statistics.mean(execution_time_3))
|
||||||
|
print("Mode:", statistics.mode(execution_time_3))
|
||||||
|
print("Median:", statistics.median(execution_time_3))
|
||||||
|
print("Standart deviation:", statistics.stdev(execution_time_3))
|
||||||
|
print()
|
||||||
|
print("Wins A*:", wins_Astar)
|
||||||
|
print("Losses A*:", losses_Astar)
|
||||||
|
print("Wins bt:", wins_bt)
|
||||||
|
print("Losses bt:", losses_bt)
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Initialize the CSV file and write the header
|
||||||
|
echo "Run,ExecutionTime_ms,Result" > execution_times.csv
|
||||||
|
|
||||||
|
FILE_NAME=Main.java
|
||||||
|
|
||||||
|
for i in {1..1000}
|
||||||
|
do
|
||||||
|
# Record the start time in milliseconds
|
||||||
|
start_time=$(perl -MTime::HiRes=time -e 'printf("%.0f\n", time()*1000)')
|
||||||
|
|
||||||
|
# Execute the Python script and capture its output
|
||||||
|
# Use `stdout` and `stderr` to capture all outputs
|
||||||
|
output=$(python "$FILE_NAME" 2>&1)
|
||||||
|
|
||||||
|
# Record the end time in milliseconds
|
||||||
|
end_time=$(perl -MTime::HiRes=time -e 'printf("%.0f\n", time()*1000)')
|
||||||
|
|
||||||
|
# Calculate the elapsed time
|
||||||
|
elapsed_time=$((end_time - start_time))
|
||||||
|
|
||||||
|
# Extract the result from the Python script's output
|
||||||
|
# Assumes the output is in the format "e <number>"
|
||||||
|
result_line=$(echo "$output" | grep '^e ')
|
||||||
|
|
||||||
|
if [ -n "$result_line" ]; then
|
||||||
|
# Extract the number after 'e '
|
||||||
|
number=$(echo "$result_line" | awk '{print $2}')
|
||||||
|
|
||||||
|
# Determine Result as 1 or 0 based on the number
|
||||||
|
if [ "$number" -gt 0 ]; then
|
||||||
|
result=1
|
||||||
|
elif [ "$number" -eq -1 ]; then
|
||||||
|
result=0
|
||||||
|
else
|
||||||
|
# Handle unexpected numbers
|
||||||
|
result="Unexpected_$number"
|
||||||
|
echo "Run $i: $elapsed_time ms, Result: $result (Unexpected number)"
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
# If the expected line is not found
|
||||||
|
result="N/A"
|
||||||
|
echo "Run $i: $elapsed_time ms, Result: $result (Missing 'e ' in output)"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Log the execution time and result
|
||||||
|
echo "Run $i: $elapsed_time ms, Result: $result"
|
||||||
|
|
||||||
|
# Append the run number, elapsed time, and result to the CSV file
|
||||||
|
echo "$i,$elapsed_time,$result" >> execution_times.csv
|
||||||
|
done
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Initialize the CSV file and write the header
|
||||||
|
echo "Run,ExecutionTime_ms" > execution_times.csv
|
||||||
|
|
||||||
|
FILE_NAME=save_neo.py
|
||||||
|
|
||||||
|
for i in {1..1000}
|
||||||
|
do
|
||||||
|
start_time=$(perl -MTime::HiRes=time -e 'printf("%.0f\n", time()*1000)')
|
||||||
|
python $FILE_NAME
|
||||||
|
end_time=$(perl -MTime::HiRes=time -e 'printf("%.0f\n", time()*1000)')
|
||||||
|
elapsed_time=$(($end_time - $start_time))
|
||||||
|
echo "Run $i: $elapsed_time ms"
|
||||||
|
# Append the result to the CSV file
|
||||||
|
echo "$i,$elapsed_time" >> execution_times.csv
|
||||||
|
done
|
||||||
+186
@@ -0,0 +1,186 @@
|
|||||||
|
import subprocess
|
||||||
|
from time import time
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import random
|
||||||
|
from dokusan import generators
|
||||||
|
|
||||||
|
# C++ коды
|
||||||
|
#code = ["./build/sudoku"]
|
||||||
|
# Java коды
|
||||||
|
code = ["java", "./Main.java"]
|
||||||
|
# Python коды
|
||||||
|
# code = ["python", "./submit.py"]
|
||||||
|
|
||||||
|
N_TESTS = 30
|
||||||
|
|
||||||
|
def read_sudoku(file):
|
||||||
|
sudoku = []
|
||||||
|
i = 0
|
||||||
|
for line in file:
|
||||||
|
i += 1
|
||||||
|
if (i == 10):
|
||||||
|
break
|
||||||
|
row = list(map(int, line.split()))
|
||||||
|
sudoku.append(row)
|
||||||
|
return sudoku
|
||||||
|
|
||||||
|
def is_valid_sudoku(sudoku, input_file):
|
||||||
|
# Проверка строк
|
||||||
|
for row in sudoku:
|
||||||
|
if len(set(row)) != 9 or any(num < 1 or num > 9 for num in row):
|
||||||
|
print('строка', row)
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Проверка столбцов
|
||||||
|
for col in range(9):
|
||||||
|
column = [sudoku[row][col] for row in range(9)]
|
||||||
|
if len(set(column)) != 9:
|
||||||
|
print('столбец', col)
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Проверка 3x3 квадратов
|
||||||
|
for box_row in range(0, 9, 3):
|
||||||
|
for box_col in range(0, 9, 3):
|
||||||
|
square = []
|
||||||
|
for i in range(3):
|
||||||
|
for j in range(3):
|
||||||
|
square.append(sudoku[box_row + i][box_col + j])
|
||||||
|
if len(set(square)) != 9:
|
||||||
|
print('квадрат')
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Проверка совпадения с input
|
||||||
|
row = 0
|
||||||
|
for line in input_file:
|
||||||
|
a = line.split()
|
||||||
|
for column in range(9):
|
||||||
|
if a[column] != '-' and int(a[column]) != sudoku[row][column]:
|
||||||
|
print('строка', row)
|
||||||
|
return False
|
||||||
|
row += 1
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def mapgen(numbers, input_file):
|
||||||
|
# Сгенерировать полный решённый Судоку
|
||||||
|
full_sudoku = list(map(int, str(generators.random_sudoku(avg_rank=0))))
|
||||||
|
grid = [full_sudoku[i:i+9] for i in range(0, 81, 9)]
|
||||||
|
|
||||||
|
# Составить список всех координат
|
||||||
|
coords = [(i, j) for i in range(9) for j in range(9)]
|
||||||
|
random.shuffle(coords)
|
||||||
|
|
||||||
|
# Удаление чисел с проверкой на уникальность решения
|
||||||
|
while sum(row.count(0) for row in grid) < (81 - numbers) and coords:
|
||||||
|
x, y = coords.pop()
|
||||||
|
grid[x][y] = 0
|
||||||
|
|
||||||
|
# Записать результат в файл
|
||||||
|
for row in grid:
|
||||||
|
input_file.write(" ".join(map(str, row)).replace('0', '-') + "\n")
|
||||||
|
|
||||||
|
def main():
|
||||||
|
exec_time_avg_easy = []
|
||||||
|
avg_fitness_avg_easy = []
|
||||||
|
max_fitness_avg_easy = []
|
||||||
|
exec_time_avg_medium = []
|
||||||
|
avg_fitness_avg_medium = []
|
||||||
|
max_fitness_avg_medium = []
|
||||||
|
exec_time_avg_hard = []
|
||||||
|
avg_fitness_avg_hard = []
|
||||||
|
max_fitness_avg_hard = []
|
||||||
|
|
||||||
|
exec_time_avg = []
|
||||||
|
avg_fitness_avg = []
|
||||||
|
max_fitness_avg = []
|
||||||
|
number_of_cells = []
|
||||||
|
a = 21
|
||||||
|
b = 41
|
||||||
|
for cells in range(a, b):
|
||||||
|
exec_time = []
|
||||||
|
avg_fitness = []
|
||||||
|
max_fitness = []
|
||||||
|
for maps in range(N_TESTS):
|
||||||
|
number_of_cells.append(cells)
|
||||||
|
|
||||||
|
# генерация карты
|
||||||
|
with open("input.txt", "w") as input_file:
|
||||||
|
mapgen(cells, input_file)
|
||||||
|
|
||||||
|
# запуск алгоритма
|
||||||
|
with open("input.txt", "r") as input_file, open("output.txt", "w") as output_file:
|
||||||
|
start = time()
|
||||||
|
process1 = subprocess.Popen(code, stdin=input_file, stdout=output_file, stderr=subprocess.PIPE, text=True)
|
||||||
|
process1.wait()
|
||||||
|
exec_time.append(round(time() - start, 2))
|
||||||
|
print('Тест', cells, maps, 'пройден за', exec_time[-1])
|
||||||
|
|
||||||
|
# проверка на корректность решения
|
||||||
|
with open("input.txt", "r") as input_file, open("output.txt", "r") as output_file:
|
||||||
|
read = output_file.readlines()
|
||||||
|
avg_fitness.append(float(read[1]))
|
||||||
|
max_fitness.append(float(read[0]))
|
||||||
|
read.pop(1)
|
||||||
|
read.pop(0)
|
||||||
|
sudoku = read_sudoku(read)
|
||||||
|
if not is_valid_sudoku(sudoku, input_file):
|
||||||
|
print("Решение судоку некорректное.")
|
||||||
|
exit()
|
||||||
|
if (30 <= cells <= 40):
|
||||||
|
exec_time_avg_easy += exec_time
|
||||||
|
avg_fitness_avg_easy += avg_fitness
|
||||||
|
max_fitness_avg_easy += max_fitness
|
||||||
|
elif (26 <= cells <= 29):
|
||||||
|
exec_time_avg_medium += exec_time
|
||||||
|
avg_fitness_avg_medium += avg_fitness
|
||||||
|
max_fitness_avg_medium += max_fitness
|
||||||
|
else:
|
||||||
|
exec_time_avg_hard += exec_time
|
||||||
|
avg_fitness_avg_hard += avg_fitness
|
||||||
|
max_fitness_avg_hard += max_fitness
|
||||||
|
exec_time_avg.append(sum(exec_time) / len(exec_time))
|
||||||
|
avg_fitness_avg.append(sum(avg_fitness) / len(avg_fitness))
|
||||||
|
max_fitness_avg.append(sum(max_fitness) / len(max_fitness))
|
||||||
|
|
||||||
|
print('EASY')
|
||||||
|
print('average time', sum(exec_time_avg_easy) / len(exec_time_avg_easy))
|
||||||
|
print('maximum fitness', sum(max_fitness_avg_easy) / len(max_fitness_avg_easy))
|
||||||
|
print('average fitness', sum(avg_fitness_avg_easy) / len(avg_fitness_avg_easy))
|
||||||
|
print()
|
||||||
|
print('MEDIUM')
|
||||||
|
print('average time', sum(exec_time_avg_medium) / len(exec_time_avg_medium))
|
||||||
|
print('maximum fitness', sum(max_fitness_avg_medium) / len(max_fitness_avg_medium))
|
||||||
|
print('average fitness', sum(avg_fitness_avg_medium) / len(avg_fitness_avg_medium))
|
||||||
|
print()
|
||||||
|
print('HARD')
|
||||||
|
print('average time', sum(exec_time_avg_hard) / len(exec_time_avg_hard))
|
||||||
|
print('maximum fitness', sum(max_fitness_avg_hard) / len(max_fitness_avg_hard))
|
||||||
|
print('average fitness', sum(avg_fitness_avg_hard) / len(avg_fitness_avg_hard))
|
||||||
|
plt.figure(1)
|
||||||
|
plt.plot([i for i in range(a, b)], avg_fitness_avg, linestyle='-', color='b')
|
||||||
|
plt.title(f'Average avg fitness on last generation among {N_TESTS} tests per each N')
|
||||||
|
plt.xlabel('Numbers provided (N)')
|
||||||
|
plt.ylabel('Average avg fitness on last generation')
|
||||||
|
plt.grid()
|
||||||
|
plt.savefig(f"avgfit{N_TESTS}.png", dpi=400)
|
||||||
|
|
||||||
|
plt.figure(2)
|
||||||
|
plt.plot([i for i in range(a, b)], exec_time_avg, linestyle='-', color='b')
|
||||||
|
plt.title(f'Average execution time among {N_TESTS} tests per each N')
|
||||||
|
plt.xlabel('Numbers provided (N)')
|
||||||
|
plt.ylabel('Average execution time, sec')
|
||||||
|
plt.grid()
|
||||||
|
plt.savefig(f"exec{N_TESTS}.png", dpi=400)
|
||||||
|
|
||||||
|
plt.figure(3)
|
||||||
|
plt.plot([i for i in range(a, b)], max_fitness_avg, linestyle='-', color='b')
|
||||||
|
plt.title(f'Average max fitness on last generation among {N_TESTS} tests per each N')
|
||||||
|
plt.xlabel('Numbers provided (N)')
|
||||||
|
plt.ylabel('Average max fitness on last generation')
|
||||||
|
plt.grid()
|
||||||
|
plt.savefig(f"maxfit{N_TESTS}.png", dpi=400)
|
||||||
|
|
||||||
|
plt.show()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
+187
@@ -0,0 +1,187 @@
|
|||||||
|
# POSSIBLE USAGE KEYS
|
||||||
|
# map, keymaker_position = Utils.generate_random_map()
|
||||||
|
# proceed with map actions...
|
||||||
|
|
||||||
|
|
||||||
|
import random
|
||||||
|
from typing import (
|
||||||
|
List,
|
||||||
|
Tuple,
|
||||||
|
Optional,
|
||||||
|
Set,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Utils:
|
||||||
|
@staticmethod
|
||||||
|
def generate_random_map() -> Tuple[List[List[str]], Optional[Tuple[int, int]]]:
|
||||||
|
"""
|
||||||
|
Generates a random 9x9 game map with placements of 'A', 'S', and 'P'.
|
||||||
|
'P' placements depend on the positions of 'A' and 'S'.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A tuple containing the game map and one unoccupied square (or None if all occupied).
|
||||||
|
"""
|
||||||
|
# Initialize a 9x9 grid with empty strings
|
||||||
|
game_map: List[List[str]] = [[[] for _ in range(9)] for _ in range(9)]
|
||||||
|
all_coordinates: List[Tuple[int, int]] = [(x, y) for x in range(9) for y in range(9)]
|
||||||
|
|
||||||
|
def place_letter(
|
||||||
|
letter: str,
|
||||||
|
count: int,
|
||||||
|
available: List[Tuple[int, int]],
|
||||||
|
) -> List[Tuple[int, int]]:
|
||||||
|
"""
|
||||||
|
Places a specified letter on the game map a certain number of times.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
letter: The letter to place ('A' or 'S').
|
||||||
|
count: Number of times to place the letter.
|
||||||
|
available: List of available coordinates.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A list of coordinates where the letter was placed.
|
||||||
|
"""
|
||||||
|
placed: List[Tuple[int, int]] = []
|
||||||
|
for _ in range(count):
|
||||||
|
if not available:
|
||||||
|
break
|
||||||
|
x, y = random.choice(available)
|
||||||
|
game_map[x][y] = [letter]
|
||||||
|
placed.append((x, y))
|
||||||
|
available.remove((x, y))
|
||||||
|
|
||||||
|
return placed
|
||||||
|
|
||||||
|
# Place "A" 0 to 3 times
|
||||||
|
num_A: int = random.randint(0, 3)
|
||||||
|
A_positions: List[Tuple[int, int]] = place_letter("A", num_A, all_coordinates)
|
||||||
|
|
||||||
|
# Place "S" 0 to 1 times
|
||||||
|
num_S: int = random.randint(0, 1)
|
||||||
|
S_positions: List[Tuple[int, int]] = place_letter("S", num_S, all_coordinates)
|
||||||
|
|
||||||
|
def get_moore_neighbors(x: int, y: int) -> List[Tuple[int, int]]:
|
||||||
|
"""
|
||||||
|
Retrieves all Moore neighbors (8 surrounding cells) for a given position.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
x: X-coordinate.
|
||||||
|
y: Y-coordinate.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A list of neighboring coordinates within bounds.
|
||||||
|
"""
|
||||||
|
neighbors: List[Tuple[int, int]] = []
|
||||||
|
for dx in [-1, 0, 1]:
|
||||||
|
for dy in [-1, 0, 1]:
|
||||||
|
if dx == 0 and dy == 0:
|
||||||
|
continue
|
||||||
|
nx, ny = x + dx, y + dy
|
||||||
|
if 0 <= nx < 9 and 0 <= ny < 9:
|
||||||
|
neighbors.append((nx, ny))
|
||||||
|
|
||||||
|
return neighbors
|
||||||
|
|
||||||
|
def get_von_neumann_neighbors(x: int, y: int) -> List[Tuple[int, int]]:
|
||||||
|
"""
|
||||||
|
Retrieves all von Neumann neighbors (4 adjacent cells) for a given position.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
x: X-coordinate.
|
||||||
|
y: Y-coordinate.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A list of neighboring coordinates within bounds.
|
||||||
|
"""
|
||||||
|
neighbors: List[Tuple[int, int]] = []
|
||||||
|
for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
|
||||||
|
nx, ny = x + dx, y + dy
|
||||||
|
if 0 <= nx < 9 and 0 <= ny < 9:
|
||||||
|
neighbors.append((nx, ny))
|
||||||
|
|
||||||
|
return neighbors
|
||||||
|
|
||||||
|
# Collect all possible P placement positions
|
||||||
|
possible_P_positions: Set[Tuple[int, int]] = set()
|
||||||
|
|
||||||
|
for x, y in A_positions:
|
||||||
|
neighbors = get_moore_neighbors(x, y)
|
||||||
|
possible_P_positions.update(neighbors)
|
||||||
|
|
||||||
|
for x, y in S_positions:
|
||||||
|
neighbors = get_von_neumann_neighbors(x, y)
|
||||||
|
possible_P_positions.update(neighbors)
|
||||||
|
|
||||||
|
# Remove positions already occupied by "A" or "S"
|
||||||
|
occupied_positions: Set[Tuple[int, int]] = set(A_positions + S_positions)
|
||||||
|
possible_P_positions = [
|
||||||
|
pos
|
||||||
|
for pos in possible_P_positions
|
||||||
|
if pos not in occupied_positions and game_map[pos[0]][pos[1]] == []
|
||||||
|
]
|
||||||
|
|
||||||
|
# Place "P" in all possible positions derived from "A" and "S"
|
||||||
|
for x, y in possible_P_positions:
|
||||||
|
game_map[x][y] = ["P"]
|
||||||
|
if (x, y) in all_coordinates:
|
||||||
|
all_coordinates.remove((x, y))
|
||||||
|
|
||||||
|
# Select one unoccupied square
|
||||||
|
chosen_unoccupied: Optional[Tuple[int, int]] = (
|
||||||
|
random.choice(all_coordinates) if all_coordinates else None
|
||||||
|
)
|
||||||
|
|
||||||
|
return game_map, chosen_unoccupied
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def heuristic(pos: Tuple[int, int], goal: Tuple[int, int]) -> int:
|
||||||
|
"""
|
||||||
|
Calculates the Manhattan distance between two positions.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pos: Current position as (x, y).
|
||||||
|
goal: Goal position as (x, y).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The Manhattan distance as an integer.
|
||||||
|
"""
|
||||||
|
return abs(pos[0] - goal[0]) + abs(pos[1] - goal[1])
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_directions(pos: Tuple[int, int]) -> List[Tuple[int, int]]:
|
||||||
|
"""
|
||||||
|
Returns possible moves (Up, Down, Left, Right) from the current position within bounds.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pos: Current position as (x, y).
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A list of valid adjacent positions.
|
||||||
|
"""
|
||||||
|
moves: List[Tuple[int, int]] = [
|
||||||
|
(pos[0] + 1, pos[1]), # Down
|
||||||
|
(pos[0] - 1, pos[1]), # Up
|
||||||
|
(pos[0], pos[1] + 1), # Right
|
||||||
|
(pos[0], pos[1] - 1), # Left
|
||||||
|
]
|
||||||
|
|
||||||
|
return [move for move in moves if 0 <= move[0] <= 8 and 0 <= move[1] <= 8]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_directions_with_zones(
|
||||||
|
pos: Tuple[int, int], enemies_perception_zones: Set[Tuple[int, int]]
|
||||||
|
) -> List[Tuple[int, int]]:
|
||||||
|
"""
|
||||||
|
Returns possible moves from the current position excluding moves that are in danger zones.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
pos: Current position as (x, y).
|
||||||
|
enemies_perception_zones: A set of dangerous positions.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A list of safe adjacent positions.
|
||||||
|
"""
|
||||||
|
moves: List[Tuple[int, int]] = Utils.get_directions(pos)
|
||||||
|
|
||||||
|
return [move for move in moves if move not in enemies_perception_zones]
|
||||||
+375
@@ -0,0 +1,375 @@
|
|||||||
|
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<Chromosome> 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<int[]> 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<Chromosome> newPopulation = new ArrayList<>();
|
||||||
|
for (int i = 0; i < mainInstance.population.size() / 2; i++) {
|
||||||
|
// Select parents using tournament selection
|
||||||
|
List<Chromosome> 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<int[]> 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<Chromosome> tournamentSelection(int tournamentSize) {
|
||||||
|
List<Chromosome> selectedParents = new ArrayList<>();
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
List<Chromosome> 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<Integer> 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<int[]> 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<int[]> 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<int[]> mutablePositions) {
|
||||||
|
this.sudoku = sudoku;
|
||||||
|
this.mutablePositions = mutablePositions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[][] getSudoku() {
|
||||||
|
return sudoku;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSudoku(int[][] sudoku) {
|
||||||
|
this.sudoku = sudoku;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<int[]> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
- 8 - - - - - 9 -
|
- - - 8 5 6 - - -
|
||||||
- - 7 5 - 2 8 - -
|
- - - 1 9 - - - -
|
||||||
6 - - 8 - 7 - - 5
|
5 - - - - 7 - 1 -
|
||||||
3 7 - - 8 - - 5 1
|
- 2 - - - 9 - 7 5
|
||||||
2 - - - - - - - 8
|
- 9 - - - 1 2 - 3
|
||||||
9 5 - - 4 - - 3 2
|
- - - - 3 - 1 - -
|
||||||
8 - - 1 - 4 - - 9
|
- 3 - - - - - 2 -
|
||||||
- - 1 9 - 3 6 - -
|
- - - - - - - - -
|
||||||
- 4 - - - - - 2 -
|
- - 1 - - - - - -
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
/home/emil/Coding/Assignments/ITAI/Sudoku_solver/other/Main.java
|
||||||
+412
@@ -0,0 +1,412 @@
|
|||||||
|
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<Chromosome> 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<int[]> 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;
|
||||||
|
List<Integer> fitnessValues = new ArrayList<>();
|
||||||
|
|
||||||
|
int stuckCounter = 0;
|
||||||
|
int tryUnstuckCount = 0;
|
||||||
|
boolean unstucking = false;
|
||||||
|
|
||||||
|
int INITIAL_POPULATIONSIZE = POPULATIONSIZE;
|
||||||
|
int INITIAL_TOURNAMENTSIZE = TOURNAMENTSIZE;
|
||||||
|
double INITIAL_MUTATIONRATE = MUTATIONRATE;
|
||||||
|
|
||||||
|
int generation = 0; // Track the number of generations
|
||||||
|
while (true) {
|
||||||
|
// Evaluate the fitness of each chromosome in the population
|
||||||
|
mainInstance.evaluatePopulation();
|
||||||
|
|
||||||
|
bestSolution = mainInstance.getBestChromosome();
|
||||||
|
fitnessValues.add(bestSolution.getFitness());
|
||||||
|
|
||||||
|
List<Chromosome> newPopulation = new ArrayList<>();
|
||||||
|
for (int i = 0; i < mainInstance.population.size() / 2; i++) {
|
||||||
|
// Select parents using tournament selection
|
||||||
|
List<Chromosome> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
//change arguments when algorithm is stuck
|
||||||
|
if (fitnessValues.size() > 2) {
|
||||||
|
if (fitnessValues.get(fitnessValues.size()-1) == fitnessValues.get(fitnessValues.size()-2)){
|
||||||
|
stuckCounter++;
|
||||||
|
//System.out.println("StuckCount: " + String.valueOf(stuckCounter));
|
||||||
|
if (stuckCounter > 50){
|
||||||
|
if (MUTATIONRATE < 1){
|
||||||
|
unstucking = true;
|
||||||
|
System.out.println(MUTATIONRATE);
|
||||||
|
System.out.println(POPULATIONSIZE);
|
||||||
|
MUTATIONRATE = MUTATIONRATE * 1.1 + 0.01;
|
||||||
|
POPULATIONSIZE /= 2;
|
||||||
|
}
|
||||||
|
stuckCounter = 0;
|
||||||
|
}
|
||||||
|
} else{
|
||||||
|
stuckCounter = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (unstucking) {
|
||||||
|
//System.out.println("Unstucking");
|
||||||
|
tryUnstuckCount++;
|
||||||
|
if (tryUnstuckCount > 100){
|
||||||
|
unstucking = false;
|
||||||
|
tryUnstuckCount = 0;
|
||||||
|
POPULATIONSIZE = INITIAL_POPULATIONSIZE;
|
||||||
|
TOURNAMENTSIZE = INITIAL_TOURNAMENTSIZE;
|
||||||
|
MUTATIONRATE = INITIAL_MUTATIONRATE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//System.out.println("UnstuckCount: " + String.valueOf(tryUnstuckCount));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate initial population of chromosomes
|
||||||
|
public void generateInitialChromosomes(int numberOfChromosomes, int[][] baseSudoku, List<int[]> 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<Chromosome> tournamentSelection(int tournamentSize) {
|
||||||
|
List<Chromosome> selectedParents = new ArrayList<>();
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
List<Chromosome> 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<Integer> 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<int[]> 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<int[]> 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<int[]> mutablePositions) {
|
||||||
|
this.sudoku = sudoku;
|
||||||
|
this.mutablePositions = mutablePositions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[][] getSudoku() {
|
||||||
|
return sudoku;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSudoku(int[][] sudoku) {
|
||||||
|
this.sudoku = sudoku;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<int[]> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||||
|
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
<parent>
|
||||||
|
<groupId>com.example</groupId>
|
||||||
|
<artifactId>SudokuSolver</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
</parent>
|
||||||
|
|
||||||
|
<groupId>com.example</groupId>
|
||||||
|
<artifactId>demo</artifactId>
|
||||||
|
<version>1.0-SNAPSHOT</version>
|
||||||
|
|
||||||
|
<properties>
|
||||||
|
<maven.compiler.source>17</maven.compiler.source>
|
||||||
|
<maven.compiler.target>17</maven.compiler.target>
|
||||||
|
</properties>
|
||||||
|
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,465 @@
|
|||||||
|
package com.example;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
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<Chromosome> 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<int[]> 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 = 500000;
|
||||||
|
TOURNAMENTSIZE = 3;
|
||||||
|
MUTATIONRATE = 0.1;
|
||||||
|
} else if (mutablePositions.size() < HARDTHRESHOLD) { // Hard sudoku
|
||||||
|
POPULATIONSIZE = 100000;
|
||||||
|
TOURNAMENTSIZE = 8;
|
||||||
|
MUTATIONRATE = 0.34;
|
||||||
|
}
|
||||||
|
else { // Ultra-hard sudoku
|
||||||
|
POPULATIONSIZE = 100000;
|
||||||
|
TOURNAMENTSIZE = 10;
|
||||||
|
MUTATIONRATE = 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate initial population of chromosomes
|
||||||
|
mainInstance.generateInitialChromosomes(POPULATIONSIZE, baseSudoku, mutablePositions);
|
||||||
|
|
||||||
|
Chromosome bestSolution = null;
|
||||||
|
List<Integer> fitnessValues = new ArrayList<>();
|
||||||
|
|
||||||
|
int generation = 0; // Track the number of generations
|
||||||
|
int stuckCounter = 0;
|
||||||
|
int tryUnstuckCount = 0;
|
||||||
|
boolean unstucking = false;
|
||||||
|
|
||||||
|
int INITIAL_POPULATIONSIZE = POPULATIONSIZE;
|
||||||
|
int INITIAL_TOURNAMENTSIZE = TOURNAMENTSIZE;
|
||||||
|
double INITIAL_MUTATIONRATE = MUTATIONRATE;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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<Chromosome> newPopulation = new ArrayList<>();
|
||||||
|
for (int i = 0; i < mainInstance.population.size() / 2; i++) {
|
||||||
|
// Select parents using tournament selection
|
||||||
|
List<Chromosome> 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;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
// Check if algorithm is stuck
|
||||||
|
if (fitnessValues.size() > 2) {
|
||||||
|
if (fitnessValues.get(fitnessValues.size()-1) == fitnessValues.get(fitnessValues.size()-2)){
|
||||||
|
stuckCounter++;
|
||||||
|
System.out.println("StuckCount: " + stuckCounter);
|
||||||
|
if (stuckCounter > 20){
|
||||||
|
if (MUTATIONRATE < 1){
|
||||||
|
unstucking = true;
|
||||||
|
System.out.println(MUTATIONRATE);
|
||||||
|
System.out.println(POPULATIONSIZE);
|
||||||
|
MUTATIONRATE = MUTATIONRATE * 1.1 + 0.01;
|
||||||
|
POPULATIONSIZE /= 2;
|
||||||
|
TOURNAMENTSIZE /= 2;
|
||||||
|
}
|
||||||
|
stuckCounter = 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
stuckCounter = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (unstucking) {
|
||||||
|
System.out.println("Unstucking");
|
||||||
|
tryUnstuckCount++;
|
||||||
|
if (tryUnstuckCount > 100){
|
||||||
|
unstucking = false;
|
||||||
|
tryUnstuckCount = 0;
|
||||||
|
POPULATIONSIZE = INITIAL_POPULATIONSIZE;
|
||||||
|
TOURNAMENTSIZE = INITIAL_TOURNAMENTSIZE;
|
||||||
|
MUTATIONRATE = INITIAL_MUTATIONRATE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
System.out.println("UnstuckCount: " + tryUnstuckCount);
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate initial population of chromosomes
|
||||||
|
public void generateInitialChromosomes(int numberOfChromosomes, int[][] baseSudoku, List<int[]> mutablePositions) {
|
||||||
|
for (int i = 0; i < numberOfChromosomes; i++) {
|
||||||
|
// Create a copy of the base Sudoku
|
||||||
|
int[][] sudoku = copyMatrix(baseSudoku);
|
||||||
|
// 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<int[]> 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));
|
||||||
|
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<Chromosome> tournamentSelection(int tournamentSize) {
|
||||||
|
List<Chromosome> selectedParents = new ArrayList<>();
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
List<Chromosome> 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<Integer> 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();
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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<int[]> 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<int[]> mutablePositions) {
|
||||||
|
this.sudoku = sudoku;
|
||||||
|
this.mutablePositions = mutablePositions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[][] getSudoku() {
|
||||||
|
return sudoku;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSudoku(int[][] sudoku) {
|
||||||
|
this.sudoku = sudoku;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<int[]> 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<Integer> 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,3 @@
|
|||||||
|
artifactId=demo
|
||||||
|
groupId=com.example
|
||||||
|
version=1.0-SNAPSHOT
|
||||||
+2
@@ -0,0 +1,2 @@
|
|||||||
|
Main$Chromosome.class
|
||||||
|
Main.class
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
/home/emil/Coding/Assignments/ITAI/Sudoku_solver/statistical/demo/src/main/java/com/example/Main.java
|
||||||
Reference in New Issue
Block a user