Merge remote-tracking branch 'origin/master'

# Conflicts:
#	app/src/main/java/sudoku/structure/MultiDoku.java
#	app/src/main/java/sudoku/structure/Sudoku.java
#	app/src/main/java/sudoku/structure/SudokuFactory.java
#	app/src/test/java/sudoku/solver/SolverTest.java
This commit is contained in:
Melvyn
2025-01-29 18:51:23 +01:00
17 changed files with 504 additions and 222 deletions

View File

@@ -5,7 +5,8 @@ import java.util.List;
/**
* Class qui représente les block de chaque sudoku,
* Un block étant un ensemble de cellule avec une contrainte de block qui lui ait associé
* Un block étant un ensemble de cellule avec une contrainte de block qui lui
* ait associé
*/
public class Block {
@@ -18,14 +19,15 @@ public class Block {
* List de sudoku qui contiennent le block
* Pour un acces plus rapide aux sudokus
*/
private List<Sudoku> sudokus;
private final List<Sudoku> sudokus;
public Block(List<Cell> cells) {
this.cells = cells;
this.sudokus = new ArrayList<>();
}
public Block() {
this.cells = new ArrayList<>();
this(new ArrayList<>());
}
public List<Cell> getCells() {
@@ -34,6 +36,7 @@ public class Block {
/**
* Ajoute une Cell au Block
*
* @param newCell Cell, à ajouter
*/
void addCell(Cell newCell) {
@@ -42,6 +45,7 @@ public class Block {
/**
* Cherche si le Block contient déjà un symbole donné.
*
* @param symbolIndex int, un index de symbole
* @return boolean, true s'il contient le symbole et false sinon
*/
@@ -65,8 +69,4 @@ public class Block {
public List<Sudoku> getSudokus() {
return sudokus;
}
void setSudokus(List<Sudoku> sudokus) {
this.sudokus = sudokus;
}
}

View File

@@ -8,7 +8,7 @@ public enum Difficulty {
double factor;
String displayName;
Difficulty(String displayName, double factor) {
private Difficulty(String displayName, double factor) {
this.factor = factor;
this.displayName = displayName;
}
@@ -21,4 +21,18 @@ public enum Difficulty {
return factor;
}
private static final String[] difficultyNames;
static {
Difficulty[] diffs = Difficulty.values();
difficultyNames = new String[diffs.length];
for (int i = 0; i < diffs.length; i++) {
difficultyNames[i] = diffs[i].getDisplayName();
}
}
public static String[] getDifficultyNames() {
return difficultyNames;
}
}

View File

@@ -1,5 +1,7 @@
package sudoku.structure;
import sudoku.constraint.BlockConstraint;
import sudoku.constraint.Constraint;
import sudoku.constraint.IConstraint;
import java.util.ArrayList;
@@ -22,13 +24,13 @@ public class Sudoku {
/**
* Liste des contraintes (TODO) du Sudoku.
*/
private final List<IConstraint> constraints;
private final List<Constraint> constraints;
/**
* Largeur des Blocks s'ils sont rectangulaires, valant 0 si ce n'est pas le cas.
*/
private int blockWidth;
public Sudoku(List<Cell> cells, List<Block> blocks, List<IConstraint> constraints) {
public Sudoku(List<Cell> cells, List<Block> blocks, List<Constraint> constraints) {
this.cells = cells;
this.blocks = blocks;
this.constraints = constraints;
@@ -41,7 +43,7 @@ public class Sudoku {
* @return Coordinate, correspondante à l'index donné.
*/
public Coordinate toCoords(int index) {
return new Coordinate( index % getSize(), index / getSize() );
return new Coordinate(index % getSize(), index / getSize());
}
/**
@@ -73,13 +75,13 @@ public class Sudoku {
/**
* Teste si on peut placer la value dans la Cell aux coordonnées x, y d'après les contraintes du Sudoku.
* @param x int, abscisse de la Cell voulue.
* @param y int, ordonnée de la Cell voulue.
* @param x int, abscisse de la Cell voulue.
* @param y int, ordonnée de la Cell voulue.
* @param value int, index du symbole qu'on veut placer.
* @return boolean, true si on peut la placer et false sinon.
*/
public boolean canBePlaced(int x, int y, int value) {
for (IConstraint constraint : this.constraints) {
for (Constraint constraint : this.constraints) {
if (!constraint.canBePlaced(this, x, y, value)) {
return false;
}
@@ -89,8 +91,8 @@ public class Sudoku {
/**
* Tente de placer le symbole value dans la Cell de coordonnées x, y.
* @param x int, abscisse de la Cell voulue.
* @param y int, coordonnée de la Cell voulue;
* @param x int, abscisse de la Cell voulue.
* @param y int, coordonnée de la Cell voulue;
* @param value int, index du symbole que l'on veut placer.
* @return boolean, true si le symbole a été placé, false sinon
*/
@@ -132,14 +134,14 @@ public class Sudoku {
/**
* Place le symbole d'index value dans la Cell de coordonnées précisées.
* @param x int, abscisse de la Cell voulue.
* @param y int, coordonnée de la Cell voulue.
* @param x int, abscisse de la Cell voulue.
* @param y int, coordonnée de la Cell voulue.
* @param value int, index du symbole à placer.
* @return Cell, la Cell qui a été modifiée.
*/
public Cell setCellSymbol(int x, int y, int value) {
assert (isValidCoords(x, y));
for (IConstraint constraint : this.constraints) {
for (Constraint constraint : this.constraints) {
if (!constraint.canBePlaced(this, x, y, value)) {
return null;
}
@@ -205,7 +207,7 @@ public class Sudoku {
return this.cells.get(i);
}
public List<IConstraint> getConstraints() {
public List<Constraint> getConstraints() {
return constraints;
}
@@ -264,8 +266,8 @@ public class Sudoku {
* Met à jour les symboles possibles des Cells du Sudoku.
*
*/
public void updateSymbolsPossibilities(){
for (IConstraint constraint : constraints) {
public void updateSymbolsPossibilities() {
for (Constraint constraint : constraints) {
List<Cell> cells = this.getCells();
for (Cell cell : cells) {
Coordinate coord = null;
@@ -275,7 +277,11 @@ public class Sudoku {
System.out.println("Cas jamais atteint.");
}
List<Integer> newPossibleSymbols = cell.getPossibleSymbols();
newPossibleSymbols.retainAll(constraint.getPossibleSymbols(this, coord.getX(), coord.getY()));
newPossibleSymbols.retainAll(constraint.getPossibleSymbols(
this,
coord.getX(),
coord.getY()
));
cell.setPossibleSymbols(newPossibleSymbols);
}
@@ -323,7 +329,7 @@ public class Sudoku {
return result;
}
for (int i = 0; i < this.constraints.size(); i++) {
IConstraint constraint = this.constraints.get(i);
Constraint constraint = this.constraints.get(i);
if (i == 0) {
result.addAll(constraint.getPossibleSymbols(this, cellCoordinates.getX(), cellCoordinates.getY()));
} else {
@@ -446,4 +452,8 @@ public class Sudoku {
this.blockWidth = blockWidth;
}
public boolean hasConstraint(Constraint constraint) {
return this.constraints.contains(constraint);
}
}

View File

@@ -1,5 +1,8 @@
package sudoku.structure;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -8,9 +11,10 @@ import java.util.Random;
import sudoku.constraint.BlockConstraint;
import sudoku.constraint.ColumnConstraint;
import sudoku.constraint.Constraint;
import sudoku.constraint.DiagonalConstraint;
import sudoku.constraint.IConstraint;
import sudoku.constraint.LineConstraint;
import sudoku.io.SudokuPrinter;
import sudoku.solver.Solver;
public class SudokuFactory {
@@ -19,18 +23,13 @@ public class SudokuFactory {
* Générateur de nombre aléatoire.
*/
private static final Random random = new Random();
/**
* Difficulté avec le ration des cases qui seront vides.
*/
private static final double VERY_EASY = 0.1;
private static final double EASY = 0.25;
private static final double MEDIUM = 0.5;
private static final double HARD = 0.75;
/**
* Liste des contraintes par défaut d'un Multi- ou Sudoku.
* Comprend les contraintes de blocs, de lignes, et de colonnes.
*/
public static List<IConstraint> DEFAULT_CONSTRAINTS = Arrays.asList(new BlockConstraint(), new LineConstraint(), new ColumnConstraint());
public static List<Constraint> DEFAULT_CONSTRAINTS = Arrays.asList(Constraint.Block, Constraint.Column,
Constraint.Line);
/**
* Créée des Cells et les met dans une liste de taille size.
@@ -81,9 +80,9 @@ public class SudokuFactory {
* @param heightBlock int, hauteur des Blocks.
* @return MultiDoku, MultiDoku vide.
*/
public static MultiDoku createBasicEmptyRectangleSudoku(int widthBlock, int heightBlock) {
Sudoku s = createRectangleSudoku(widthBlock, heightBlock);
return new MultiDoku(Arrays.asList(s));
public static MultiDoku createBasicEmptyRectangleDoku(int widthBlock, int heightBlock,
List<Constraint> constraints) {
return new MultiDoku(Arrays.asList(createRectangleSudoku(widthBlock, heightBlock, constraints)));
}
/**
@@ -91,13 +90,13 @@ public class SudokuFactory {
* @param size int, taille des Blocks.
* @return MultiDoku, MultiDoku vide.
*/
public static MultiDoku createBasicEmptySquareSudoku(int size) {
return createBasicEmptyRectangleSudoku(size, size);
public static MultiDoku createBasicEmptySquareDoku(int size, List<Constraint> constraints) {
return new MultiDoku(Arrays.asList(createSquareSudoku(size, constraints)));
}
/**
* Place des Cells immutables de valeurs fournies, aux Coordinate fournies dans le MultiDoku doku fourni.
* @param doku MultiDoku, MultiDoku à remplir.
* @param doku MultiDoku, MultiDoku à remplir.
* @param immutableCells Map<Coordinate, Integer>, association de Coordinate coordonnées et Integer valeurs, correspondant aux cases à remplir.
*/
public static void setImmutableCells(MultiDoku doku, Map<Coordinate, Integer> immutableCells) {
@@ -120,7 +119,7 @@ public class SudokuFactory {
* @return boolean, valant true si un MultiDoku de difficulté donnée peut être créée, false sinon.
* @throws Exception si la difficulté n'est pas compatible avec la taille du MultiDoku.
*/
public static boolean newDokuFromFilledOne (MultiDoku doku, int nbCellsToEmpty) throws Exception {
public static boolean newDokuFromFilledOne(MultiDoku doku, int nbCellsToEmpty) throws Exception {
System.out.println("nbCellsToEmpty : "+nbCellsToEmpty);
if (nbCellsToEmpty >= doku.getCells().size()) {
@@ -152,37 +151,40 @@ public class SudokuFactory {
}
return false;
}
}
/**
* Créée un Sudoku vide dont les Blocks sont de taille widthBlock par heightBlock.
* @param widthBlock int, largeur des Blocks.
* @param widthBlock int, largeur des Blocks.
* @param heightBlock int, hauteur des Blocks.
* @return Sudoku, Sudoku vide.
*/
private static Sudoku createRectangleSudoku(int widthBlock, int heightBlock) {
private static Sudoku createRectangleSudoku(int widthBlock, int heightBlock, List<Constraint> constraints) {
int symbolCount = widthBlock * heightBlock;
List<Cell> cases = initCells(symbolCount);
List<Block> blocs = initRectangleBlocs(cases, widthBlock, heightBlock);
Sudoku s = new Sudoku(cases, blocs, DEFAULT_CONSTRAINTS);
Sudoku s = new Sudoku(cases, blocs, constraints);
for (Block block : s.getBlocks()) {
block.getSudokus().add(s);
}
s.setBlockWidth(widthBlock);
return s;
}
/**
/**
* Créée un Sudoku vide dont les Blocks sont carrés de longueur size.
* @param size int, taille des Blocks.
* @return Sudoku, Sudoku vide.
*/
private static Sudoku createSquareSudoku(int size) {
return createRectangleSudoku(size, size);
private static Sudoku createSquareSudoku(int size, List<Constraint> constraints) {
return createRectangleSudoku(size, size, constraints);
}
/**
* Connecte deux Sudokus selon la décalage offset fourni.
* @param sudoku1 Sudoku, premier sudoku à connecter.
* @param sudoku2 Sudoku, second sudoku à connecter.
* @param offset Coordinate, décalage entre les deux Sudokus.
* @param offset Coordinate, décalage entre les deux Sudokus.
*/
private static void linkSquareSudokus(Sudoku sudoku1, Sudoku sudoku2, Coordinate offset) {
int blockWidth = sudoku1.getBlockWidth();
@@ -199,6 +201,7 @@ public class SudokuFactory {
// on remplace le bloc
sudoku2.getBlocks().set(block2Y * blockWidth + block2X, block1);
block1.getSudokus().add(sudoku2);
// on remplace les cellules
for (int i = 0; i < block1.getCells().size(); i++) {
@@ -218,7 +221,7 @@ public class SudokuFactory {
* @param size int, largeur des Blocks unitraires des Sudokus à crééer.
* @return MultiDoku, MultiDoku de forme X.
*/
public static MultiDoku createBasicXShapedMultidoku(int size) {
public static MultiDoku createBasicXShapedMultidoku(int size, List<Constraint> constraints) {
assert (size > 1);
/*
@@ -227,11 +230,11 @@ public class SudokuFactory {
* 4 5
*/
Sudoku sudoku1 = createSquareSudoku(size);
Sudoku sudoku2 = createSquareSudoku(size);
Sudoku sudoku3 = createSquareSudoku(size);
Sudoku sudoku4 = createSquareSudoku(size);
Sudoku sudoku5 = createSquareSudoku(size);
Sudoku sudoku1 = createSquareSudoku(size, constraints);
Sudoku sudoku2 = createSquareSudoku(size, constraints);
Sudoku sudoku3 = createSquareSudoku(size, constraints);
Sudoku sudoku4 = createSquareSudoku(size, constraints);
Sudoku sudoku5 = createSquareSudoku(size, constraints);
linkSquareSudokus(sudoku1, sudoku2, new Coordinate(1 - size, 1 - size));
linkSquareSudokus(sudoku1, sudoku3, new Coordinate(size - 1, 1 - size));
@@ -243,15 +246,22 @@ public class SudokuFactory {
public static void fillDoku(MultiDoku doku, Difficulty difficulty) throws Exception {
Solver.solveRandom(doku, random);
//SudokuPrinter.printRectangleSudoku(doku.getSubGrid(0), 3, 3);
int nbCellsToEmpty = (int)(difficulty.getFactor()*doku.getNbCells());
//System.out.println(nbCellsToEmpty);
boolean successful = newDokuFromFilledOne(doku, nbCellsToEmpty);
if (!successful) {
int nbCellsToEmpty = (int) (difficulty.getFactor() * doku.getNbCells());
boolean successfull = newDokuFromFilledOne(doku, nbCellsToEmpty);
if (!successfull) {
throw new Exception("Canno't create this doku with this difficulty");
}
doku.setFilledCellsImmutable();
}
public static MultiDoku fromfile(String filePath) {
try {
String content = Files.readString(Paths.get(filePath));
MultiDoku doku = SudokuSerializer.deserializeSudoku(content);
return doku;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}