Merge branch 'master' of git.ale-pri.com:Ryuk/Sudoku
Some checks failed
Linux arm64 / Build (push) Has been cancelled
Some checks failed
Linux arm64 / Build (push) Has been cancelled
This commit is contained in:
5
app/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
5
app/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
distributionBase=GRADLE_USER_HOME
|
||||||
|
distributionPath=wrapper/dists
|
||||||
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip
|
||||||
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
zipStorePath=wrapper/dists
|
||||||
@@ -3,6 +3,7 @@ package gui.menu;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
import java.util.concurrent.CancellationException;
|
import java.util.concurrent.CancellationException;
|
||||||
|
|
||||||
import gui.ColorGenerator;
|
import gui.ColorGenerator;
|
||||||
@@ -12,7 +13,7 @@ import imgui.ImVec2;
|
|||||||
import imgui.ImVec4;
|
import imgui.ImVec4;
|
||||||
import imgui.flag.ImGuiCol;
|
import imgui.flag.ImGuiCol;
|
||||||
import imgui.flag.ImGuiStyleVar;
|
import imgui.flag.ImGuiStyleVar;
|
||||||
import sudoku.solver.StupidSolver;
|
import sudoku.solver.Solver;
|
||||||
import sudoku.structure.Block;
|
import sudoku.structure.Block;
|
||||||
import sudoku.structure.Cell;
|
import sudoku.structure.Cell;
|
||||||
import sudoku.structure.MultiDoku;
|
import sudoku.structure.MultiDoku;
|
||||||
@@ -98,18 +99,17 @@ public class SudokuView extends BaseView {
|
|||||||
ImGui.beginDisabled();
|
ImGui.beginDisabled();
|
||||||
|
|
||||||
if (ImGui.button("Résoudre")) {
|
if (ImGui.button("Résoudre")) {
|
||||||
resolveThread = new Thread(new Runnable() {
|
|
||||||
@Override
|
resolveThread = new Thread(() -> {
|
||||||
public void run() {
|
|
||||||
try {
|
try {
|
||||||
|
Random rand = new Random();
|
||||||
doku.getSubGrid(0).clear();
|
doku.getSubGrid(0).clear();
|
||||||
StupidSolver.solve(doku);
|
Solver.solveRandom(doku, rand);
|
||||||
Thread.sleep(200);
|
Thread.sleep(200);
|
||||||
} catch (CancellationException | InterruptedException e) {
|
} catch (CancellationException | InterruptedException e) {
|
||||||
System.out.println("The user is bored !");
|
System.out.println("The user is bored !");
|
||||||
}
|
}
|
||||||
stopResolve();
|
stopResolve();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
boolean wantsToStop = false;
|
boolean wantsToStop = false;
|
||||||
|
|||||||
@@ -4,16 +4,79 @@ import sudoku.structure.MultiDoku;
|
|||||||
import sudoku.structure.Cell;
|
import sudoku.structure.Cell;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
import java.util.concurrent.CancellationException;
|
||||||
|
|
||||||
public class Solver {
|
public class Solver {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Résout le multidoku passé en paramètre si c'est possible.
|
* Résout le multidoku passé en paramètre si c'est possible.
|
||||||
* En testant toutes les possibilités avec un algorithme de backtracking.
|
* En testant toutes les possibilités, de manière aléatoire, avec un algorithme de backtracking.
|
||||||
|
*
|
||||||
* @param doku Multidoku, à résoudre
|
* @param doku Multidoku, à résoudre
|
||||||
* @return boolean, true s'il est résolut ou false s'il ne l'est pas.
|
* @param rand Random, pour tester aléatoirement les symboles
|
||||||
|
* @return boolean, true s'il est résolu ou false s'il ne l'est pas.
|
||||||
*/
|
*/
|
||||||
|
public static boolean solveRandom(MultiDoku doku, Random rand) {
|
||||||
|
if (Thread.interrupted())
|
||||||
|
throw new CancellationException("User wants to stop the solver");
|
||||||
|
|
||||||
|
if (doku.isValid()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cell cellToFill = doku.getFirstEmptyCell();
|
||||||
|
if (cellToFill == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Integer> possibleSymbols = doku.getPossibleSymbolsOfCell(cellToFill);
|
||||||
|
|
||||||
|
while (!possibleSymbols.isEmpty()){
|
||||||
|
int nextPossibleSymbolIndex = rand.nextInt(possibleSymbols.size());
|
||||||
|
int nextSymbol = possibleSymbols.get(nextPossibleSymbolIndex);
|
||||||
|
|
||||||
|
cellToFill.setSymbolIndex(nextSymbol);
|
||||||
|
if (Solver.solveRandom(doku, rand)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
cellToFill.setSymbolIndex(Cell.NOSYMBOL);
|
||||||
|
possibleSymbols.remove(nextPossibleSymbolIndex);
|
||||||
|
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int countSolution(MultiDoku doku) {
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
if (doku.isValid()) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Cell cellToFill = doku.getFirstEmptyCell();
|
||||||
|
if (cellToFill == null) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Integer> possibleSymbols = doku.getPossibleSymbolsOfCell(cellToFill);
|
||||||
|
|
||||||
|
for (int symbol : possibleSymbols) {
|
||||||
|
|
||||||
|
cellToFill.setSymbolIndex(symbol);
|
||||||
|
if (Solver.solve(doku)) {
|
||||||
|
result++;
|
||||||
|
}
|
||||||
|
cellToFill.setSymbolIndex(Cell.NOSYMBOL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean solve(MultiDoku doku) {
|
public static boolean solve(MultiDoku doku) {
|
||||||
|
if (Thread.interrupted())
|
||||||
|
throw new CancellationException("User wants to stop the solver");
|
||||||
|
|
||||||
if (doku.isValid()) {
|
if (doku.isValid()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -29,45 +92,15 @@ public class Solver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (int symbol : possibleSymbols){
|
for (int symbol : possibleSymbols){
|
||||||
|
|
||||||
cellToFill.setSymbolIndex(symbol);
|
cellToFill.setSymbolIndex(symbol);
|
||||||
if (Solver.solve(doku)) {
|
if (Solver.solve(doku)) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
cellToFill.setSymbolIndex(Cell.NOSYMBOL);
|
cellToFill.setSymbolIndex(Cell.NOSYMBOL);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Ancien algo abandonné pour le moment
|
|
||||||
|
|
||||||
private void rollBack() {
|
|
||||||
stack.pop();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public MultiDoku solve(MultiDoku doku, List<IConstraint> constraints) throws Exception {
|
|
||||||
List<MutableCell> allMutableCells = doku.getMutableCells();
|
|
||||||
List<MutableCell> remainingCellsToCheck = new ArrayList<>(allMutableCells);
|
|
||||||
Random rand = new Random();
|
|
||||||
while (!remainingCellsToCheck.isEmpty()) {
|
|
||||||
int indexCurrentCell = rand.nextInt(remainingCellsToCheck.size());
|
|
||||||
MutableCell currentCell = remainingCellsToCheck.get(indexCurrentCell);
|
|
||||||
|
|
||||||
int symbol = currentCell.getPossibleSymbols().get(0);
|
|
||||||
currentCell.setSymbolIndex(symbol);
|
|
||||||
// stack.push(new MutableCell(currentCell));
|
|
||||||
try {
|
|
||||||
doku.updateSymbolsPossibilities();
|
|
||||||
} catch (Exception e) {
|
|
||||||
this.rollBack();
|
|
||||||
System.out.println(this.stack);
|
|
||||||
}
|
|
||||||
remainingCellsToCheck.remove(indexCurrentCell);
|
|
||||||
}
|
|
||||||
return doku;
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import java.util.concurrent.CancellationException;
|
|||||||
import sudoku.structure.MultiDoku;
|
import sudoku.structure.MultiDoku;
|
||||||
import sudoku.structure.Sudoku;
|
import sudoku.structure.Sudoku;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class de test non utilisé
|
||||||
|
*/
|
||||||
public class StupidSolver {
|
public class StupidSolver {
|
||||||
|
|
||||||
private static boolean solve(Sudoku sudoku, int index) {
|
private static boolean solve(Sudoku sudoku, int index) {
|
||||||
|
|||||||
@@ -77,4 +77,10 @@ public class Cell {
|
|||||||
public boolean isMutable() {
|
public boolean isMutable() {
|
||||||
return this.isMutable;
|
return this.isMutable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int empty() {
|
||||||
|
int oldSymbol = this.symbolIndex;
|
||||||
|
this.symbolIndex = Cell.NOSYMBOL;
|
||||||
|
return oldSymbol;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -98,5 +98,21 @@ public class MultiDoku {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Cell> getFilledCells() {
|
||||||
|
List<Cell> result = new ArrayList<>();
|
||||||
|
for (Cell c : this.getCells()){
|
||||||
|
if (!c.isEmpty()) {
|
||||||
|
result.add(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void empty(Cell cell) {
|
||||||
|
List<Cell> cells = getCells();
|
||||||
|
Cell cellToEmpty = cells.get(cells.indexOf(cell));
|
||||||
|
cellToEmpty.setSymbolIndex(Cell.NOSYMBOL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -242,8 +242,9 @@ public class Sudoku {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
for (IConstraint constraint : this.constraints) {
|
for (int i = 0; i < this.constraints.size(); i++) {
|
||||||
if (result.isEmpty()) {
|
IConstraint constraint = this.constraints.get(i);
|
||||||
|
if (i == 0) {
|
||||||
result.addAll(constraint.getPossibleSymbols(this, cellCoordinates.getX(), cellCoordinates.getY()));
|
result.addAll(constraint.getPossibleSymbols(this, cellCoordinates.getX(), cellCoordinates.getY()));
|
||||||
} else {
|
} else {
|
||||||
result.retainAll(constraint.getPossibleSymbols(this, cellCoordinates.getX(), cellCoordinates.getY()));
|
result.retainAll(constraint.getPossibleSymbols(this, cellCoordinates.getX(), cellCoordinates.getY()));
|
||||||
|
|||||||
@@ -4,14 +4,17 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
import sudoku.constraint.BlockConstraint;
|
import sudoku.constraint.BlockConstraint;
|
||||||
import sudoku.constraint.ColumnConstraint;
|
import sudoku.constraint.ColumnConstraint;
|
||||||
import sudoku.constraint.IConstraint;
|
import sudoku.constraint.IConstraint;
|
||||||
import sudoku.constraint.LineConstraint;
|
import sudoku.constraint.LineConstraint;
|
||||||
|
import sudoku.solver.Solver;
|
||||||
|
|
||||||
public class SudokuFactory {
|
public class SudokuFactory {
|
||||||
|
|
||||||
|
private static final Random random = new Random();
|
||||||
public static List<IConstraint> DEFAULT_CONSTRAINTS = Arrays.asList(new BlockConstraint(), new LineConstraint(), new ColumnConstraint());
|
public static List<IConstraint> DEFAULT_CONSTRAINTS = Arrays.asList(new BlockConstraint(), new LineConstraint(), new ColumnConstraint());
|
||||||
|
|
||||||
private static List<Cell> initCells(int size) {
|
private static List<Cell> initCells(int size) {
|
||||||
@@ -66,6 +69,37 @@ public class SudokuFactory {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean newDokuFromFilledOne (MultiDoku doku, int difficulty) throws Exception {
|
||||||
|
|
||||||
|
if (difficulty > doku.getCells().size()) {
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (difficulty == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Cell> cellsThatCanBeEmptied = doku.getFilledCells();
|
||||||
|
|
||||||
|
while (!cellsThatCanBeEmptied.isEmpty()) {
|
||||||
|
int index = random.nextInt(cellsThatCanBeEmptied.size());
|
||||||
|
Cell cellToEmpty = cellsThatCanBeEmptied.get(index);
|
||||||
|
|
||||||
|
int oldSymbol = cellToEmpty.empty();
|
||||||
|
|
||||||
|
if (Solver.countSolution(doku) == 1) {
|
||||||
|
if (newDokuFromFilledOne(doku, --difficulty)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cellToEmpty.setSymbolIndex(oldSymbol);
|
||||||
|
cellsThatCanBeEmptied.remove(cellToEmpty);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private static Sudoku createRectangleSudoku(int width, int height) {
|
private static Sudoku createRectangleSudoku(int width, int height) {
|
||||||
int symbolCount = width * height;
|
int symbolCount = width * height;
|
||||||
List<Cell> cases = initCells(symbolCount);
|
List<Cell> cases = initCells(symbolCount);
|
||||||
|
|||||||
@@ -8,11 +8,13 @@ import sudoku.structure.Sudoku;
|
|||||||
import sudoku.structure.SudokuFactory;
|
import sudoku.structure.SudokuFactory;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
class SolverTest {
|
class SolverTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void solveTest() {
|
void solveTest() {
|
||||||
|
Random rand = new Random();
|
||||||
|
|
||||||
MultiDoku dokuToTest = SudokuFactory.createBasicEmptySquareSudoku(3);
|
MultiDoku dokuToTest = SudokuFactory.createBasicEmptySquareSudoku(3);
|
||||||
MultiDoku dokuResult = SudokuFactory.createBasicEmptySquareSudoku(3);
|
MultiDoku dokuResult = SudokuFactory.createBasicEmptySquareSudoku(3);
|
||||||
@@ -56,7 +58,7 @@ class SolverTest {
|
|||||||
|
|
||||||
assert(dokuResult.isValid());
|
assert(dokuResult.isValid());
|
||||||
|
|
||||||
Solver.solve(dokuToTest);
|
Solver.solveRandom(dokuToTest, rand);
|
||||||
|
|
||||||
|
|
||||||
System.out.println("\n****************************\nDoku solved");
|
System.out.println("\n****************************\nDoku solved");
|
||||||
@@ -81,8 +83,14 @@ class SolverTest {
|
|||||||
5, ns, ns, ns, 3, 1, 0, ns, ns);
|
5, ns, ns, ns, 3, 1, 0, ns, ns);
|
||||||
sudokuToTest2.setImmutableCellsSymbol(immutableCells2);
|
sudokuToTest2.setImmutableCellsSymbol(immutableCells2);
|
||||||
|
|
||||||
boolean isSolved = Solver.solve(dokuToTest2);
|
boolean isSolved = Solver.solveRandom(dokuToTest2, rand);
|
||||||
|
|
||||||
assert(!isSolved);
|
assert(!isSolved);
|
||||||
|
|
||||||
|
MultiDoku dokuToTest3 = SudokuFactory.createBasicEmptySquareSudoku(3);
|
||||||
|
|
||||||
|
Solver.solveRandom(dokuToTest3, rand);
|
||||||
|
|
||||||
|
SudokuPrinter.printRectangleSudoku(dokuToTest3.getSubGrid(0), 3, 3);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user