feat: dynamic constraints (Fixes #8)
All checks were successful
Linux arm64 / Build (push) Successful in 37s

This commit is contained in:
2025-01-29 17:19:44 +01:00
parent 5e26bea609
commit c16f2b8f5a
12 changed files with 311 additions and 149 deletions

View File

@@ -1,8 +1,10 @@
package gui; package gui;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import common.Signal; import common.Signal;
import gui.ColorGenerator.Color; import gui.ColorGenerator.Color;
@@ -11,10 +13,11 @@ 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 imgui.flag.ImGuiWindowFlags; import sudoku.constraint.Constraint;
import sudoku.structure.Block; import sudoku.structure.Block;
import sudoku.structure.Cell; import sudoku.structure.Cell;
import sudoku.structure.MultiDoku; import sudoku.structure.MultiDoku;
import sudoku.structure.Sudoku;
public class SudokuRenderer { public class SudokuRenderer {
@@ -24,13 +27,28 @@ public class SudokuRenderer {
private static final ImVec4 BLACK = new ImVec4(0, 0, 0, 1); private static final ImVec4 BLACK = new ImVec4(0, 0, 0, 1);
private static final ImVec4 TRANSPARENT = new ImVec4(); private static final ImVec4 TRANSPARENT = new ImVec4();
private static final ImVec4 WHITE = new ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
private static final ImVec2 cellSize = new ImVec2(50, 50); private static final ImVec2 cellSize = new ImVec2(50, 50);
private final Set<Cell> diagonals = new HashSet<>();
public final Signal onResolve = new Signal(); public final Signal onResolve = new Signal();
public SudokuRenderer(MultiDoku doku) { public SudokuRenderer(MultiDoku doku) {
this.doku = RenderableMultidoku.fromMultidoku(doku); this.doku = RenderableMultidoku.fromMultidoku(doku);
this.colorPalette = initColors(); this.colorPalette = initColors();
initDiagonals();
}
private void initDiagonals() {
for (Sudoku sudoku : this.doku.getDoku().getSubGrids()) {
if (sudoku.hasConstraint(Constraint.Diagonal)) {
for (int i = 0; i < sudoku.getSize(); i++) {
this.diagonals.add(sudoku.getCell(i, i));
this.diagonals.add(sudoku.getCell(sudoku.getSize() - i - 1, i));
}
}
}
} }
private Map<Block, Color> initColors() { private Map<Block, Color> initColors() {
@@ -91,13 +109,16 @@ public class SudokuRenderer {
ImGui.pushStyleColor(ImGuiCol.Border, TRANSPARENT); ImGui.pushStyleColor(ImGuiCol.Border, TRANSPARENT);
ImGui.pushStyleColor(ImGuiCol.Button, TRANSPARENT); ImGui.pushStyleColor(ImGuiCol.Button, TRANSPARENT);
ImGui.button("##" + index, cellSize); ImGui.button("##" + index, cellSize);
} else {
if (diagonals.contains(cell)) {
ImGui.pushStyleColor(ImGuiCol.Border, WHITE);
} else { } else {
ImGui.pushStyleColor(ImGuiCol.Border, BLACK); ImGui.pushStyleColor(ImGuiCol.Border, BLACK);
}
int symbol = cell.getSymbolIndex(); int symbol = cell.getSymbolIndex();
Color blockColor = colorPalette.get(cell.getBlock()); Color blockColor = colorPalette.get(cell.getBlock());
if (!cell.isMutable()) { if (!cell.isMutable()) {
blockColor = new Color(blockColor.r - 0.20f, blockColor.g - 0.20f, blockColor.b - 0.20f); blockColor = new Color(blockColor.r - 0.20f, blockColor.g - 0.20f, blockColor.b - 0.20f);
} else {
} }
ImGui.pushStyleColor(ImGuiCol.Button, new ImVec4(blockColor.r, blockColor.g, blockColor.b, 1.0f)); ImGui.pushStyleColor(ImGuiCol.Button, new ImVec4(blockColor.r, blockColor.g, blockColor.b, 1.0f));
String cellText = ""; String cellText = "";

View File

@@ -1,10 +1,15 @@
package gui; package gui;
import java.util.ArrayList;
import java.util.List;
import common.Signal; import common.Signal;
import imgui.ImGui; import imgui.ImGui;
import imgui.extension.imguifiledialog.ImGuiFileDialog; import imgui.extension.imguifiledialog.ImGuiFileDialog;
import imgui.extension.imguifiledialog.flag.ImGuiFileDialogFlags; import imgui.extension.imguifiledialog.flag.ImGuiFileDialogFlags;
import imgui.type.ImBoolean;
import imgui.type.ImInt; import imgui.type.ImInt;
import sudoku.constraint.Constraint;
import sudoku.structure.Difficulty; import sudoku.structure.Difficulty;
import sudoku.structure.MultiDoku; import sudoku.structure.MultiDoku;
import sudoku.structure.SudokuFactory; import sudoku.structure.SudokuFactory;
@@ -19,7 +24,7 @@ public class SudokuSelector {
private final ImInt sudokuType = new ImInt(0); private final ImInt sudokuType = new ImInt(0);
private final ImInt difficulty = new ImInt(Difficulty.Medium.ordinal()); private final ImInt difficulty = new ImInt(Difficulty.Medium.ordinal());
private final String[] difficulties; private final List<ImBoolean> contraints = new ArrayList<>();
private static final String[] sudokuTypes = { "Carré", "Rectangle", "Multidoku" }; private static final String[] sudokuTypes = { "Carré", "Rectangle", "Multidoku" };
private static final int SQUARE = 0, RECTANGLE = 1, MULTIDOKU = 2; private static final int SQUARE = 0, RECTANGLE = 1, MULTIDOKU = 2;
@@ -31,10 +36,21 @@ public class SudokuSelector {
public SudokuSelector(boolean canGenEmptyGrid) { public SudokuSelector(boolean canGenEmptyGrid) {
this.canGenEmptyGrid = canGenEmptyGrid; this.canGenEmptyGrid = canGenEmptyGrid;
Difficulty[] diffs = Difficulty.values(); initConstraints();
difficulties = new String[diffs.length]; }
for (int i = 0; i < diffs.length; i++) {
difficulties[i] = diffs[i].getDisplayName(); private List<Constraint> getConstraints() {
List<Constraint> constraints = new ArrayList<>();
for (int i = 0; i < this.contraints.size(); i++) {
if (this.contraints.get(i).get())
constraints.add(Constraint.values()[i]);
}
return constraints;
}
private void initConstraints() {
for (Constraint cons : Constraint.values()) {
contraints.add(new ImBoolean(SudokuFactory.DEFAULT_CONSTRAINTS.contains(cons)));
} }
} }
@@ -71,15 +87,21 @@ public class SudokuSelector {
public void render() { public void render() {
ImGui.combo("Type de Sudoku", sudokuType, sudokuTypes); ImGui.combo("Type de Sudoku", sudokuType, sudokuTypes);
ImGui.combo("Difficulté", difficulty, difficulties); ImGui.combo("Difficulté", difficulty, Difficulty.getDifficultyNames());
if (ImGui.treeNode("Constraintes")) {
for (Constraint cons : Constraint.values()) {
ImGui.checkbox(cons.getDisplayName(), contraints.get(cons.ordinal()));
}
ImGui.treePop();
}
switch (sudokuType.get()) { switch (sudokuType.get()) {
case SQUARE: case SQUARE:
ImGui.inputInt("Taille", sudokuSize); ImGui.inputInt("Taille", sudokuSize);
if (ImGui.button("Résoudre un sudoku")) { if (ImGui.button("Résoudre un sudoku")) {
selectSudoku(SudokuFactory.createBasicEmptySquareSudoku(sudokuSize.get()), false); selectSudoku(SudokuFactory.createBasicEmptySquareDoku(sudokuSize.get(), getConstraints()), false);
} }
if (canGenEmptyGrid && ImGui.button("Générer une grille vide")) { if (canGenEmptyGrid && ImGui.button("Générer une grille vide")) {
selectSudoku(SudokuFactory.createBasicEmptySquareSudoku(sudokuSize.get()), true); selectSudoku(SudokuFactory.createBasicEmptySquareDoku(sudokuSize.get(), getConstraints()), true);
} }
break; break;
@@ -88,22 +110,25 @@ public class SudokuSelector {
ImGui.inputInt("Longueur", sudokuWidth); ImGui.inputInt("Longueur", sudokuWidth);
if (ImGui.button("Résoudre un sudoku")) { if (ImGui.button("Résoudre un sudoku")) {
selectSudoku( selectSudoku(
SudokuFactory.createBasicEmptyRectangleSudoku(sudokuWidth.get(), sudokuHeight.get()), SudokuFactory.createBasicEmptyRectangleDoku(sudokuWidth.get(), sudokuHeight.get(),
getConstraints()),
false); false);
} }
if (canGenEmptyGrid && ImGui.button("Générer une grille vide")) { if (canGenEmptyGrid && ImGui.button("Générer une grille vide")) {
selectSudoku( selectSudoku(
SudokuFactory.createBasicEmptyRectangleSudoku(sudokuWidth.get(), sudokuHeight.get()), true); SudokuFactory.createBasicEmptyRectangleDoku(sudokuWidth.get(), sudokuHeight.get(),
getConstraints()),
true);
} }
break; break;
case MULTIDOKU: case MULTIDOKU:
ImGui.inputInt("Taille", sudokuSize); ImGui.inputInt("Taille", sudokuSize);
if (ImGui.button("Résoudre un sudoku")) { if (ImGui.button("Résoudre un sudoku")) {
selectSudoku(SudokuFactory.createBasicXShapedMultidoku(sudokuSize.get()), false); selectSudoku(SudokuFactory.createBasicXShapedMultidoku(sudokuSize.get(), getConstraints()), false);
} }
if (canGenEmptyGrid && ImGui.button("Générer une grille vide")) { if (canGenEmptyGrid && ImGui.button("Générer une grille vide")) {
selectSudoku(SudokuFactory.createBasicXShapedMultidoku(sudokuSize.get()), true); selectSudoku(SudokuFactory.createBasicXShapedMultidoku(sudokuSize.get(), getConstraints()), true);
} }
default: default:

View File

@@ -17,7 +17,8 @@ public class MultiPlayerView extends BaseView {
this.client = client; this.client = client;
this.server = server; this.server = server;
this.client.onDisconnect.connect(this::onDisconnect); this.client.onDisconnect.connect(this::onDisconnect);
this.client.onGameStarted.connect(() -> this.stateMachine.pushState(new MultiPlayerDokuView(stateMachine, client, server))); this.client.onGameStarted
.connect(() -> this.stateMachine.pushState(new MultiPlayerDokuView(stateMachine, client, server)));
} }
@Override @Override
@@ -36,7 +37,7 @@ public class MultiPlayerView extends BaseView {
} else { } else {
if (ImGui.button("Démarrer")) { if (ImGui.button("Démarrer")) {
// temp // temp
MultiDoku doku = SudokuFactory.createBasicXShapedMultidoku(3); MultiDoku doku = SudokuFactory.createBasicXShapedMultidoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
this.server.startGame(doku); this.server.startGame(doku);
} }
} }

View File

@@ -18,30 +18,29 @@ public class Main {
int blockWidth = 2; int blockWidth = 2;
int blockHeight = 2; int blockHeight = 2;
var multidoku = SudokuFactory.createBasicEmptyRectangleSudoku(blockWidth, blockHeight); var multidoku = SudokuFactory.createBasicEmptyRectangleDoku(blockWidth, blockHeight,
SudokuFactory.DEFAULT_CONSTRAINTS);
var sudoku = multidoku.getSubGrid(0); var sudoku = multidoku.getSubGrid(0);
if(!sudoku.setCellsSymbol(Arrays.asList(0,1,2,3, 2,3,1,1, 1,0,3,2, 3,2,1,1))){ if (!sudoku.setCellsSymbol(Arrays.asList(0, 1, 2, 3, 2, 3, 1, 1, 1, 0, 3, 2, 3, 2, 1, 1))) {
System.out.println("At least one of those values does not respect the constraints."); System.out.println("At least one of those values does not respect the constraints.");
} }
// sudoku.setCellSymbol(8,3,0);
//sudoku.setCellSymbol(8,3,0); SudokuPrinter.printRectangleSudoku(multidoku.getSubGrid(0), blockWidth, blockHeight);
SudokuPrinter.printRectangleSudoku(multidoku.getSubGrid(0), blockWidth , blockHeight);
/* /*
Solver solver = new Solver(); * Solver solver = new Solver();
ArrayList<IConstraint> constraints = new ArrayList<>(); * ArrayList<IConstraint> constraints = new ArrayList<>();
constraints.add(new LineConstraint()); * constraints.add(new LineConstraint());
constraints.add(new ColumnConstraint()); * constraints.add(new ColumnConstraint());
constraints.add(new BlockConstraint()); * constraints.add(new BlockConstraint());
try { * try {
solver.solve(multidoku, constraints); * solver.solve(multidoku, constraints);
} catch (Exception e) { * } catch (Exception e) {
System.out.println(e); * System.out.println(e);
} * }
*/ */
} }
} }

View File

@@ -0,0 +1,52 @@
package sudoku.constraint;
import java.util.List;
import sudoku.structure.Sudoku;
public enum Constraint {
Block("Bloc", new BlockConstraint()),
Column("Colonne", new ColumnConstraint()),
Line("Ligne", new LineConstraint()),
Diagonal("Diagonale", new DiagonalConstraint());
String displayName;
IConstraint constraint;
private Constraint(String displayName, IConstraint contraint) {
this.constraint = contraint;
this.displayName = displayName;
}
public boolean canBePlaced(Sudoku s, int x, int y, int newValue) {
return getConstraint().canBePlaced(s, x, y, newValue);
}
public List<Integer> getPossibleSymbols(final Sudoku s, int x, int y) {
return getConstraint().getPossibleSymbols(s, x, y);
}
public String getDisplayName() {
return displayName;
}
public IConstraint getConstraint() {
return constraint;
}
private static final String[] constraintNames;
static {
Constraint[] cons = Constraint.values();
constraintNames = new String[cons.length];
for (int i = 0; i < cons.length; i++) {
constraintNames[i] = cons[i].getDisplayName();
}
}
public static String[] getConstraintNames() {
return constraintNames;
}
}

View File

@@ -13,7 +13,7 @@ public class DiagonalConstraint implements IConstraint {
} }
} else if (s.getSize() - x == y) { } else if (s.getSize() - x == y) {
for (int i = 0; i < s.getSize(); i++) { for (int i = 0; i < s.getSize(); i++) {
if (s.getCell(s.getSize() - i, i).getSymbolIndex() == newSymbolIndex) if (s.getCell(s.getSize() - i - 1, i).getSymbolIndex() == newSymbolIndex)
return false; return false;
} }
} }

View File

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

View File

@@ -8,7 +8,7 @@ public enum Difficulty {
double factor; double factor;
String displayName; String displayName;
Difficulty(String displayName, double factor) { private Difficulty(String displayName, double factor) {
this.factor = factor; this.factor = factor;
this.displayName = displayName; this.displayName = displayName;
} }
@@ -21,4 +21,18 @@ public enum Difficulty {
return factor; 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; package sudoku.structure;
import sudoku.constraint.BlockConstraint;
import sudoku.constraint.Constraint;
import sudoku.constraint.IConstraint; import sudoku.constraint.IConstraint;
import java.util.ArrayList; import java.util.ArrayList;
@@ -22,13 +24,14 @@ public class Sudoku {
/** /**
* Liste des contraintes (TODO) du 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. * Largeur des Blocks s'ils sont rectangulaires, valant 0 si ce n'est pas le
* cas.
*/ */
private int blockWidth; 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.cells = cells;
this.blocks = blocks; this.blocks = blocks;
this.constraints = constraints; this.constraints = constraints;
@@ -41,11 +44,12 @@ public class Sudoku {
* @return Coordinate, correspondante à l'index donné. * @return Coordinate, correspondante à l'index donné.
*/ */
public Coordinate toCoords(int index) { public Coordinate toCoords(int index) {
return new Coordinate( index % getSize(), index / getSize() ); return new Coordinate(index % getSize(), index / getSize());
} }
/** /**
* Transforme des coordonées d'une Cell en index. * Transforme des coordonées d'une Cell en index.
*
* @param x int, abscisse. * @param x int, abscisse.
* @param y int, ordonnée. * @param y int, ordonnée.
* @return int, index correspondant. * @return int, index correspondant.
@@ -56,7 +60,9 @@ public class Sudoku {
/** /**
* Vérifie que des coordonnées correspondent bien à une Cell dans le Sudoku. * Vérifie que des coordonnées correspondent bien à une Cell dans le Sudoku.
* @return boolean, valant true si les coordonnées sont dans les bornes du Sudoku, false sinon. *
* @return boolean, valant true si les coordonnées sont dans les bornes du
* Sudoku, false sinon.
*/ */
public boolean isValidCoords(int x, int y) { public boolean isValidCoords(int x, int y) {
int index = toIndex(x, y); int index = toIndex(x, y);
@@ -65,21 +71,25 @@ public class Sudoku {
/** /**
* Vérifie que l'index correspond bien à une Cell dans le Sudoku. * Vérifie que l'index correspond bien à une Cell dans le Sudoku.
* @return boolean, valant true si l'index est dans les bornes du Sudoku, false sinon. *
* @return boolean, valant true si l'index est dans les bornes du Sudoku, false
* sinon.
*/ */
public boolean isValidCoords(int index) { public boolean isValidCoords(int index) {
return index < getSize() * getSize(); return index < getSize() * getSize();
} }
/** /**
* Teste si on peut placer la value dans la Cell aux coordonnées x, y d'après les contraintes du 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 x int, abscisse de la Cell voulue.
* @param y int, ordonnée de la Cell voulue. * @param y int, ordonnée de la Cell voulue.
* @param value int, index du symbole qu'on veut placer. * @param value int, index du symbole qu'on veut placer.
* @return boolean, true si on peut la placer et false sinon. * @return boolean, true si on peut la placer et false sinon.
*/ */
public boolean canBePlaced(int x, int y, int value) { 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)) { if (!constraint.canBePlaced(this, x, y, value)) {
return false; return false;
} }
@@ -89,6 +99,7 @@ public class Sudoku {
/** /**
* Tente de placer le symbole value dans la Cell de coordonnées x, y. * Tente de placer le symbole value dans la Cell de coordonnées x, y.
*
* @param x int, abscisse de la Cell voulue. * @param x int, abscisse de la Cell voulue.
* @param y int, coordonnée de la Cell voulue; * @param y int, coordonnée de la Cell voulue;
* @param value int, index du symbole que l'on veut placer. * @param value int, index du symbole que l'on veut placer.
@@ -105,6 +116,7 @@ public class Sudoku {
/** /**
* Vide la Cell dotn les coordonnées sont renseignées de son symbole. * Vide la Cell dotn les coordonnées sont renseignées de son symbole.
*
* @param x int, abscisse de la Cell voulue. * @param x int, abscisse de la Cell voulue.
* @param y int, coordonnée de la Cell voulue. * @param y int, coordonnée de la Cell voulue.
*/ */
@@ -132,6 +144,7 @@ public class Sudoku {
/** /**
* Place le symbole d'index value dans la Cell de coordonnées précisées. * Place le symbole d'index value dans la Cell de coordonnées précisées.
*
* @param x int, abscisse de la Cell voulue. * @param x int, abscisse de la Cell voulue.
* @param y int, coordonnée de la Cell voulue. * @param y int, coordonnée de la Cell voulue.
* @param value int, index du symbole à placer. * @param value int, index du symbole à placer.
@@ -139,7 +152,7 @@ public class Sudoku {
*/ */
public Cell setCellSymbol(int x, int y, int value) { public Cell setCellSymbol(int x, int y, int value) {
assert (isValidCoords(x, y)); assert (isValidCoords(x, y));
for (IConstraint constraint : this.constraints) { for (Constraint constraint : this.constraints) {
if (!constraint.canBePlaced(this, x, y, value)) { if (!constraint.canBePlaced(this, x, y, value)) {
return null; return null;
} }
@@ -151,6 +164,7 @@ public class Sudoku {
/** /**
* Place les symboles d'index contenus dans values dans les cases du Sudoku. * Place les symboles d'index contenus dans values dans les cases du Sudoku.
*
* @param values List<Integer>, liste des index des symboles à placer. * @param values List<Integer>, liste des index des symboles à placer.
* @return boolean, vaut true si les symboles ont été placés, false sinon. * @return boolean, vaut true si les symboles ont été placés, false sinon.
*/ */
@@ -168,7 +182,9 @@ public class Sudoku {
} }
/** /**
* Place les symboles d'index contenus dans values dans les cases du Sudoku et rend ces cases immuables. * Place les symboles d'index contenus dans values dans les cases du Sudoku et
* rend ces cases immuables.
*
* @param values List<Integer>, liste des index des symboles à placer. * @param values List<Integer>, liste des index des symboles à placer.
* @return boolean, vaut true si les symboles ont été placés, false sinon. * @return boolean, vaut true si les symboles ont été placés, false sinon.
*/ */
@@ -205,7 +221,7 @@ public class Sudoku {
return this.cells.get(i); return this.cells.get(i);
} }
public List<IConstraint> getConstraints() { public List<Constraint> getConstraints() {
return constraints; return constraints;
} }
@@ -223,6 +239,7 @@ public class Sudoku {
/** /**
* Vérifie si une Cell appartient au Sudoku. * Vérifie si une Cell appartient au Sudoku.
*
* @param cell Cell, cellule dont on veut vérifier l'appartenance au Sudoku. * @param cell Cell, cellule dont on veut vérifier l'appartenance au Sudoku.
* @return boolean, vaut true si la Cell appartient au Sudoku. * @return boolean, vaut true si la Cell appartient au Sudoku.
*/ */
@@ -232,6 +249,7 @@ public class Sudoku {
/** /**
* Localise la Cell dans le Sudoku. * Localise la Cell dans le Sudoku.
*
* @param c Cell, cellule dont on veut les coordonées. * @param c Cell, cellule dont on veut les coordonées.
* @return Coordinate, coordonnées de la Cell. * @return Coordinate, coordonnées de la Cell.
* @throws Exception si la Cell n'appartient pas au Sudoku. * @throws Exception si la Cell n'appartient pas au Sudoku.
@@ -264,8 +282,8 @@ public class Sudoku {
* Met à jour les symboles possibles des Cells du Sudoku. * Met à jour les symboles possibles des Cells du Sudoku.
* *
*/ */
public void updateSymbolsPossibilities(){ public void updateSymbolsPossibilities() {
for (IConstraint constraint : constraints) { for (Constraint constraint : constraints) {
List<Cell> cells = this.getCells(); List<Cell> cells = this.getCells();
for (Cell cell : cells) { for (Cell cell : cells) {
Coordinate coord = null; Coordinate coord = null;
@@ -275,7 +293,8 @@ public class Sudoku {
System.out.println("Cas jamais atteint."); System.out.println("Cas jamais atteint.");
} }
List<Integer> newPossibleSymbols = cell.getPossibleSymbols(); List<Integer> newPossibleSymbols = cell.getPossibleSymbols();
newPossibleSymbols.retainAll(constraint.getPossibleSymbols(this, coord.getX(), coord.getY())); newPossibleSymbols
.retainAll(constraint.getConstraint().getPossibleSymbols(this, coord.getX(), coord.getY()));
cell.setPossibleSymbols(newPossibleSymbols); cell.setPossibleSymbols(newPossibleSymbols);
} }
@@ -298,6 +317,7 @@ public class Sudoku {
/** /**
* Renvoie la 1re Cell vide du Sudoku. * Renvoie la 1re Cell vide du Sudoku.
*
* @return Cell, une Cell vide, ou null s'il n'y en a pas. * @return Cell, une Cell vide, ou null s'il n'y en a pas.
*/ */
public Cell getFirstEmptyCell() { public Cell getFirstEmptyCell() {
@@ -311,8 +331,10 @@ public class Sudoku {
/** /**
* Renvoie l'index des symboles possibles de la Cell passée en paramètres. * Renvoie l'index des symboles possibles de la Cell passée en paramètres.
*
* @param cellToFill Cell, cellule dont on cherche les symboles posisbles. * @param cellToFill Cell, cellule dont on cherche les symboles posisbles.
* @return List<Integer>, la liste des index des symboles possibles, vide si la Cell n'appartient pas au Sudoku. * @return List<Integer>, la liste des index des symboles possibles, vide si la
* Cell n'appartient pas au Sudoku.
*/ */
public List<Integer> getPossibleSymbolsOfCell(Cell cellToFill) { public List<Integer> getPossibleSymbolsOfCell(Cell cellToFill) {
List<Integer> result = new ArrayList<>(); List<Integer> result = new ArrayList<>();
@@ -323,7 +345,7 @@ public class Sudoku {
return result; return result;
} }
for (int i = 0; i < this.constraints.size(); i++) { for (int i = 0; i < this.constraints.size(); i++) {
IConstraint constraint = this.constraints.get(i); Constraint constraint = this.constraints.get(i);
if (i == 0) { if (i == 0) {
result.addAll(constraint.getPossibleSymbols(this, cellCoordinates.getX(), cellCoordinates.getY())); result.addAll(constraint.getPossibleSymbols(this, cellCoordinates.getX(), cellCoordinates.getY()));
} else { } else {
@@ -335,7 +357,9 @@ public class Sudoku {
/** /**
* Vérifie que le Sudoku est cohérent avec ses contraintes. * Vérifie que le Sudoku est cohérent avec ses contraintes.
* @return boolean, valant true si le Sudoku est cohérent avec ses contraintes, false sinon. *
* @return boolean, valant true si le Sudoku est cohérent avec ses contraintes,
* false sinon.
*/ */
public boolean isValid() { public boolean isValid() {
for (Cell cell : this.cells) { for (Cell cell : this.cells) {
@@ -343,7 +367,7 @@ public class Sudoku {
if (cell.isEmpty()) { if (cell.isEmpty()) {
return false; return false;
} }
for (IConstraint constraint : this.constraints) { for (Constraint constraint : this.constraints) {
Coordinate coords; Coordinate coords;
try { try {
int symbolPlaced = cell.getSymbolIndex(); int symbolPlaced = cell.getSymbolIndex();
@@ -389,4 +413,8 @@ public class Sudoku {
this.blockWidth = blockWidth; this.blockWidth = blockWidth;
} }
public boolean hasConstraint(Constraint constraint) {
return this.constraints.contains(constraint);
}
} }

View File

@@ -11,6 +11,8 @@ import java.util.Random;
import sudoku.constraint.BlockConstraint; import sudoku.constraint.BlockConstraint;
import sudoku.constraint.ColumnConstraint; import sudoku.constraint.ColumnConstraint;
import sudoku.constraint.Constraint;
import sudoku.constraint.DiagonalConstraint;
import sudoku.constraint.IConstraint; import sudoku.constraint.IConstraint;
import sudoku.constraint.LineConstraint; import sudoku.constraint.LineConstraint;
import sudoku.io.SudokuSerializer; import sudoku.io.SudokuSerializer;
@@ -27,10 +29,12 @@ public class SudokuFactory {
* Liste des contraintes par défaut d'un Multi- ou Sudoku. * Liste des contraintes par défaut d'un Multi- ou Sudoku.
* Comprend les contraintes de blocs, de lignes, et de colonnes. * 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. * Créée des Cells et les met dans une liste de taille size.
*
* @param size int, nombre de Cells à initialiser. * @param size int, nombre de Cells à initialiser.
* @return List<Cell>, liste des Cells initialisées. * @return List<Cell>, liste des Cells initialisées.
*/ */
@@ -43,7 +47,9 @@ public class SudokuFactory {
} }
/** /**
* Créée des Blocks de taille width par height à partir des cellules données, et les met dans une liste. * Créée des Blocks de taille width par height à partir des cellules données, et
* les met dans une liste.
*
* @param cells List<Cell>, liste des Cells à découper en Blocks. * @param cells List<Cell>, liste des Cells à découper en Blocks.
* @param width int, largeur des Blocks à créer. * @param width int, largeur des Blocks à créer.
* @param height int, hauteur des Blocks à créer. * @param height int, hauteur des Blocks à créer.
@@ -73,29 +79,36 @@ public class SudokuFactory {
} }
/** /**
* Créée un MultiDoku vide dont les Blocks sont de taille widthBlock par heightBlock. * Créée un MultiDoku 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. * @param heightBlock int, hauteur des Blocks.
* @return MultiDoku, MultiDoku vide. * @return MultiDoku, MultiDoku vide.
*/ */
public static MultiDoku createBasicEmptyRectangleSudoku(int widthBlock, int heightBlock) { public static MultiDoku createBasicEmptyRectangleDoku(int widthBlock, int heightBlock,
Sudoku s = createRectangleSudoku(widthBlock, heightBlock); List<Constraint> constraints) {
return new MultiDoku(Arrays.asList(s)); return new MultiDoku(Arrays.asList(createRectangleSudoku(widthBlock, heightBlock, constraints)));
} }
/** /**
* Créée un MultiDoku vide dont les Blocks sont carrés de longueur size. * Créée un MultiDoku vide dont les Blocks sont carrés de longueur size.
*
* @param size int, taille des Blocks. * @param size int, taille des Blocks.
* @return MultiDoku, MultiDoku vide. * @return MultiDoku, MultiDoku vide.
*/ */
public static MultiDoku createBasicEmptySquareSudoku(int size) { public static MultiDoku createBasicEmptySquareDoku(int size, List<Constraint> constraints) {
return createBasicEmptyRectangleSudoku(size, size); return new MultiDoku(Arrays.asList(createSquareSudoku(size, constraints)));
} }
/** /**
* Place des Cells immutables de valeurs fournies, aux Coordinate fournies dans le MultiDoku doku fourni. * 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. * @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) { public static void setImmutableCells(MultiDoku doku, Map<Coordinate, Integer> immutableCells) {
immutableCells.forEach((coordinate, symbol) -> { immutableCells.forEach((coordinate, symbol) -> {
@@ -114,10 +127,12 @@ public class SudokuFactory {
* *
* @param doku MultiDoku, MultiDoku dont on doit vider des Cells. * @param doku MultiDoku, MultiDoku dont on doit vider des Cells.
* @param nbCellsToEmpty int, nombre de cases à retirer. * @param nbCellsToEmpty int, nombre de cases à retirer.
* @return boolean, valant true si un MultiDoku de difficulté donnée peut être créée, false sinon. * @return boolean, valant true si un MultiDoku de difficulté donnée peut être
* @throws Exception si la difficulté n'est pas compatible avec la taille du MultiDoku. * 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 {
if (nbCellsToEmpty > doku.getCells().size()) { if (nbCellsToEmpty > doku.getCells().size()) {
throw new Exception(); throw new Exception();
@@ -149,31 +164,38 @@ public class SudokuFactory {
} }
/** /**
* Créée un Sudoku vide dont les Blocks sont de taille widthBlock par heightBlock. * 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. * @param heightBlock int, hauteur des Blocks.
* @return Sudoku, Sudoku vide. * @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; int symbolCount = widthBlock * heightBlock;
List<Cell> cases = initCells(symbolCount); List<Cell> cases = initCells(symbolCount);
List<Block> blocs = initRectangleBlocs(cases, widthBlock, heightBlock); 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); s.setBlockWidth(widthBlock);
return s; return s;
} }
/** /**
* Créée un Sudoku vide dont les Blocks sont carrés de longueur size. * Créée un Sudoku vide dont les Blocks sont carrés de longueur size.
*
* @param size int, taille des Blocks. * @param size int, taille des Blocks.
* @return Sudoku, Sudoku vide. * @return Sudoku, Sudoku vide.
*/ */
private static Sudoku createSquareSudoku(int size) { private static Sudoku createSquareSudoku(int size, List<Constraint> constraints) {
return createRectangleSudoku(size, size); return createRectangleSudoku(size, size, constraints);
} }
/** /**
* Connecte deux Sudokus selon la décalage offset fourni. * Connecte deux Sudokus selon la décalage offset fourni.
*
* @param sudoku1 Sudoku, premier sudoku à connecter. * @param sudoku1 Sudoku, premier sudoku à connecter.
* @param sudoku2 Sudoku, second 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.
@@ -193,6 +215,7 @@ public class SudokuFactory {
// on remplace le bloc // on remplace le bloc
sudoku2.getBlocks().set(block2Y * blockWidth + block2X, block1); sudoku2.getBlocks().set(block2Y * blockWidth + block2X, block1);
block1.getSudokus().add(sudoku2);
// on remplace les cellules // on remplace les cellules
for (int i = 0; i < block1.getCells().size(); i++) { for (int i = 0; i < block1.getCells().size(); i++) {
@@ -208,11 +231,14 @@ public class SudokuFactory {
} }
/** /**
* Créée un MultiDoku de Blocks carrés de taille size composé de cinq Sudokus, dont un central qui partage chacun de ses Blockss d'angle avec un autre Sudoku. * Créée un MultiDoku de Blocks carrés de taille size composé de cinq Sudokus,
* dont un central qui partage chacun de ses Blockss d'angle avec un autre
* Sudoku.
*
* @param size int, largeur des Blocks unitraires des Sudokus à crééer. * @param size int, largeur des Blocks unitraires des Sudokus à crééer.
* @return MultiDoku, MultiDoku de forme X. * @return MultiDoku, MultiDoku de forme X.
*/ */
public static MultiDoku createBasicXShapedMultidoku(int size) { public static MultiDoku createBasicXShapedMultidoku(int size, List<Constraint> constraints) {
assert (size > 1); assert (size > 1);
/* /*
@@ -221,11 +247,11 @@ public class SudokuFactory {
* 4 5 * 4 5
*/ */
Sudoku sudoku1 = createSquareSudoku(size); Sudoku sudoku1 = createSquareSudoku(size, constraints);
Sudoku sudoku2 = createSquareSudoku(size); Sudoku sudoku2 = createSquareSudoku(size, constraints);
Sudoku sudoku3 = createSquareSudoku(size); Sudoku sudoku3 = createSquareSudoku(size, constraints);
Sudoku sudoku4 = createSquareSudoku(size); Sudoku sudoku4 = createSquareSudoku(size, constraints);
Sudoku sudoku5 = createSquareSudoku(size); Sudoku sudoku5 = createSquareSudoku(size, constraints);
linkSquareSudokus(sudoku1, sudoku2, new Coordinate(1 - size, 1 - size)); linkSquareSudokus(sudoku1, sudoku2, new Coordinate(1 - size, 1 - size));
linkSquareSudokus(sudoku1, sudoku3, new Coordinate(size - 1, 1 - size)); linkSquareSudokus(sudoku1, sudoku3, new Coordinate(size - 1, 1 - size));
@@ -237,7 +263,7 @@ public class SudokuFactory {
public static void fillDoku(MultiDoku doku, Difficulty difficulty) throws Exception { public static void fillDoku(MultiDoku doku, Difficulty difficulty) throws Exception {
Solver.solveRandom(doku, random); Solver.solveRandom(doku, random);
int nbCellsToEmpty = (int)(difficulty.getFactor()*doku.getNbCells()); int nbCellsToEmpty = (int) (difficulty.getFactor() * doku.getNbCells());
boolean successfull = newDokuFromFilledOne(doku, nbCellsToEmpty); boolean successfull = newDokuFromFilledOne(doku, nbCellsToEmpty);
if (!successfull) { if (!successfull) {
throw new Exception("Canno't create this doku with this difficulty"); throw new Exception("Canno't create this doku with this difficulty");

View File

@@ -13,14 +13,16 @@ import sudoku.structure.SudokuFactory;
public class SudokuSerializerTest { public class SudokuSerializerTest {
void testSerializeWithSize(int blockWidth, int blockHeight) { void testSerializeWithSize(int blockWidth, int blockHeight) {
var sudoku = SudokuFactory.createBasicEmptyRectangleSudoku(blockWidth, blockHeight); var sudoku = SudokuFactory.createBasicEmptyRectangleDoku(blockWidth, blockHeight,
SudokuFactory.DEFAULT_CONSTRAINTS);
JSONObject data = SudokuSerializer.serializeSudoku(sudoku); JSONObject data = SudokuSerializer.serializeSudoku(sudoku);
MultiDoku multiDoku = SudokuSerializer.deserializeSudoku(data); MultiDoku multiDoku = SudokuSerializer.deserializeSudoku(data);
assert(data.toString().equals(SudokuSerializer.serializeSudoku(multiDoku).toString())); assert (data.toString().equals(SudokuSerializer.serializeSudoku(multiDoku).toString()));
} }
void testSaveWithSize(int blockWidth, int blockHeight) { void testSaveWithSize(int blockWidth, int blockHeight) {
MultiDoku doku = SudokuFactory.createBasicEmptyRectangleSudoku(blockWidth, blockHeight); MultiDoku doku = SudokuFactory.createBasicEmptyRectangleDoku(blockWidth, blockHeight,
SudokuFactory.DEFAULT_CONSTRAINTS);
String savePath = SudokuSerializer.saveMultiDoku(doku); String savePath = SudokuSerializer.saveMultiDoku(doku);
MultiDoku otherDoku = null; MultiDoku otherDoku = null;
try { try {
@@ -33,7 +35,7 @@ public class SudokuSerializerTest {
e.printStackTrace(); e.printStackTrace();
assert false; assert false;
} }
assert(doku.equals(otherDoku)); assert (doku.equals(otherDoku));
} }
@Test @Test

View File

@@ -16,8 +16,8 @@ class SolverTest {
void solveTest() { void solveTest() {
Random rand = new Random(); Random rand = new Random();
MultiDoku dokuToTest = SudokuFactory.createBasicEmptySquareSudoku(3); MultiDoku dokuToTest = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
MultiDoku dokuResult = SudokuFactory.createBasicEmptySquareSudoku(3); MultiDoku dokuResult = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
Sudoku sudokuToTest = dokuToTest.getSubGrid(0); Sudoku sudokuToTest = dokuToTest.getSubGrid(0);
Sudoku sudokuResult = dokuResult.getSubGrid(0); Sudoku sudokuResult = dokuResult.getSubGrid(0);
@@ -33,12 +33,10 @@ class SolverTest {
8, 0, ns, 7, ns, 5, 2, ns, 3, 8, 0, ns, 7, ns, 5, 2, ns, 3,
5, ns, ns, ns, 3, 1, 0, ns, ns); 5, ns, ns, ns, 3, 1, 0, ns, ns);
assert(sudokuToTest.setImmutableCellsSymbol(immutableCells)); assert (sudokuToTest.setImmutableCellsSymbol(immutableCells));
SudokuPrinter.printRectangleSudoku(dokuToTest.getSubGrid(0), 3, 3); SudokuPrinter.printRectangleSudoku(dokuToTest.getSubGrid(0), 3, 3);
List<Integer> correctCells = List.of(7, 6, 0, 3, 4, 2, 8, 5, 1, List<Integer> correctCells = List.of(7, 6, 0, 3, 4, 2, 8, 5, 1,
2, 3, 8, 1, 5, 6, 7, 0, 4, 2, 3, 8, 1, 5, 6, 7, 0, 4,
1, 4, 5, 8, 0, 7, 3, 2, 6, 1, 4, 5, 8, 0, 7, 3, 2, 6,
@@ -51,25 +49,21 @@ class SolverTest {
sudokuResult.setCellsSymbol(correctCells); sudokuResult.setCellsSymbol(correctCells);
System.out.println("\n****************************Doku Control\n"); System.out.println("\n****************************Doku Control\n");
SudokuPrinter.printRectangleSudoku(sudokuResult, 3, 3); SudokuPrinter.printRectangleSudoku(sudokuResult, 3, 3);
assert (dokuResult.isValid());
assert(dokuResult.isValid());
Solver.solveRandom(dokuToTest, rand); Solver.solveRandom(dokuToTest, rand);
System.out.println("\n****************************\nDoku solved"); System.out.println("\n****************************\nDoku solved");
SudokuPrinter.printRectangleSudoku(dokuToTest.getSubGrid(0), 3, 3); SudokuPrinter.printRectangleSudoku(dokuToTest.getSubGrid(0), 3, 3);
assert (dokuToTest.isValid());
assert(dokuToTest.isValid()); assert (dokuToTest.equals(dokuResult));
assert(dokuToTest.equals(dokuResult)); MultiDoku dokuToTest2 = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
MultiDoku dokuToTest2 = SudokuFactory.createBasicEmptySquareSudoku(3);
Sudoku sudokuToTest2 = dokuToTest2.getSubGrid(0); Sudoku sudokuToTest2 = dokuToTest2.getSubGrid(0);
List<Integer> immutableCells2 = List.of(ns, ns, 0, ns, ns, 2, 8, ns, 1, List<Integer> immutableCells2 = List.of(ns, ns, 0, ns, ns, 2, 8, ns, 1,
@@ -85,9 +79,9 @@ class SolverTest {
boolean isSolved = Solver.solveRandom(dokuToTest2, rand); boolean isSolved = Solver.solveRandom(dokuToTest2, rand);
assert(!isSolved); assert (!isSolved);
MultiDoku dokuToTest3 = SudokuFactory.createBasicEmptySquareSudoku(3); MultiDoku dokuToTest3 = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
Solver.solveRandom(dokuToTest3, rand); Solver.solveRandom(dokuToTest3, rand);