2 Commits

Author SHA1 Message Date
67da77af2e feat: add states
Some checks failed
Linux arm64 / Build (push) Has been cancelled
2025-01-30 11:54:23 +01:00
c4becf2d55 fix: serialize 2025-01-30 11:54:21 +01:00
12 changed files with 254 additions and 415 deletions

View File

@@ -27,10 +27,6 @@ public class RenderableMultidoku {
this.doku = doku;
}
public boolean isResolved() {
return this.doku.isSolved();
}
public int getWidth() {
return width;
}
@@ -51,21 +47,7 @@ public class RenderableMultidoku {
return cells.get(index);
}
public boolean setCellValue(Cell cell, int value) {
for (Sudoku s : doku.getSubGrids()) {
int cellIndex = s.getCells().indexOf(cell);
// la cellule existe
if (cellIndex != -1) {
int cellX = cellIndex % s.getSize();
int cellY = cellIndex / s.getSize();
if (!s.canBePlaced(cellX, cellY, value)) {
return false;
}
}
}
cell.setSymbolIndex(value);
return true;
}
private static record PositionConstraint(Sudoku sudoku1, Sudoku sudoku2, Coordinate offset) {
}

View File

@@ -76,8 +76,8 @@ public class SudokuRenderer {
}
} else {
if (ImGui.button(Integer.toString(i + 1), cellSize)) {
this.doku.setCellValue(currentCell, i);
if (this.doku.isResolved())
currentCell.trySetValue(i);
if (this.doku.getDoku().isSolved())
this.onResolve.emit();
ImGui.closeCurrentPopup();
}

View File

@@ -1,7 +1,8 @@
package sudoku.io;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
@@ -15,16 +16,15 @@ import sudoku.structure.Block;
import sudoku.structure.Cell;
import sudoku.structure.MultiDoku;
import sudoku.structure.Sudoku;
import sudoku.structure.SudokuFactory;
public class SudokuSerializer {
public static JSONObject serializeSudoku(final MultiDoku multidoku) {
List<Cell> cellIds = new ArrayList<>();
List<Cell> cellIds = new ArrayList<>();
List<Block> blockIds = new ArrayList<>();
JSONObject jsonRoot = new JSONObject();
JSONArray jsonCells = new JSONArray();
JSONObject jsonRoot = new JSONObject();
JSONArray jsonCells = new JSONArray();
JSONArray jsonBlocks = new JSONArray();
JSONArray jsonSudokus = new JSONArray(multidoku.getNbSubGrids());
@@ -33,9 +33,10 @@ public class SudokuSerializer {
// init cells
for (Cell cell : sudoku.getCells()) {
if (!cellIds.contains(cell)) {
cellIds.add(cell);
}
if (cellIds.contains(cell))
continue;
cellIds.add(cell);
Block block = cell.getBlock();
if (!blockIds.contains(block)) {
@@ -43,6 +44,7 @@ public class SudokuSerializer {
}
int blockID = blockIds.indexOf(block);
assert(blockID >= 0);
int symbolIndex = cell.getSymbolIndex();
JSONObject cellJsonObject = new JSONObject();
@@ -58,16 +60,17 @@ public class SudokuSerializer {
// init blocks
for (Block blockId : blockIds) {
JSONObject blockJsonObject = new JSONObject();
JSONArray cellsJsonArray = new JSONArray();
for (Cell cell : blockId.getCells()) {
int cellID = cellIds.indexOf(cell);
cellsJsonArray.put(cellID);
}
blockJsonObject.put("cellIDs", cellsJsonArray);
jsonBlocks.put(blockJsonObject);
}
for (Block blockId : blockIds) {
JSONObject blockJsonObject = new JSONObject();
JSONArray cellsJsonArray = new JSONArray();
for (Cell cell : blockId.getCells()) {
int cellID = cellIds.indexOf(cell);
assert (cellID >= 0);
cellsJsonArray.put(cellID);
}
blockJsonObject.put("cellIDs", cellsJsonArray);
jsonBlocks.put(blockJsonObject);
}
for (int i = 0; i < multidoku.getNbSubGrids(); i++) {
// serialise sub grid
@@ -82,6 +85,7 @@ public class SudokuSerializer {
for (Cell cell : sudoku.getCells()) {
int cellID = cellIds.indexOf(cell);
assert (cellID >= 0);
cellsJsonArray.put(cellID);
}
@@ -89,6 +93,7 @@ public class SudokuSerializer {
for (Block block : sudoku.getBlocks()) {
int blockID = blockIds.indexOf(block);
assert (blockID >= 0);
blocksJsonArray.put(blockID);
}
@@ -114,6 +119,7 @@ public class SudokuSerializer {
/**
* Save a serialized MultiDoku in a JSON file.
*
* @param doku MultiDoku, MultiDoku to save.
* @return String, the path of the save.
*/
@@ -139,6 +145,7 @@ public class SudokuSerializer {
/**
* Get a MultiDoku from a pre-existing json save file.
*
* @param numberSave int, number of the save file to open.
* @return MultiDoku, MultoDoku contained in the file.
* @throws Exception when the given save file does not exist.
@@ -158,20 +165,6 @@ public class SudokuSerializer {
throw new Exception("This save does not exist.");
} else {
fileContent = new String(Files.readAllBytes(Paths.get("save/" + fileName)));
/*
try {
FileReader file = new FileReader(f);
char[] rawFileContent = {};
int length = 1000;
while (file.read(rawFileContent, 0, length) != -1) {
rawFileContent = new char[]{};
length = length * 10;
}
fileContent = new String(rawFileContent);
} catch (IOException e) {
throw new Exception("Error reading file.");
}
*/
return deserializeSudoku(fileContent);
}
}

View File

@@ -1,16 +1,16 @@
package sudoku.solver;
import sudoku.io.SudokuPrinter;
import sudoku.structure.MultiDoku;
import sudoku.structure.Cell;
import sudoku.structure.Sudoku;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CancellationException;
import java.util.logging.Level;
import java.util.logging.Logger;
import sudoku.io.SudokuPrinter;
import sudoku.structure.Cell;
import sudoku.structure.MultiDoku;
import sudoku.structure.Sudoku;
public class Solver {
/**
@@ -46,7 +46,7 @@ public class Solver {
return false;
}
List<Integer> possibleSymbols = doku.getPossibleSymbolsOfCell(cellToFill);
List<Integer> possibleSymbols = cellToFill.getPossibleSymbols();
while (!possibleSymbols.isEmpty()) {
int nextPossibleSymbolIndex = rand.nextInt(possibleSymbols.size());
@@ -68,31 +68,26 @@ public class Solver {
* @param oldDoku MultiDoku, MultiDoku dont on veut le nombre de solutions.
* @return int, nombre de solutions possibles.
*/
public static int countSolution(MultiDoku oldDoku) {
public static int countSolution(MultiDoku doku) {
int result = 0;
MultiDoku doku = oldDoku.clone();
if (doku.isSolved()) {
return 1;
}
Cell cellToFill = doku.getFirstEmptyCell();
if (cellToFill == null) {
System.out.println("AAAAAAAAAAAAAA");
return 0;
}
assert(cellToFill != null);
List<Integer> possibleSymbols = doku.getPossibleSymbolsOfCell(cellToFill);
List<Integer> possibleSymbols = cellToFill.getPossibleSymbols();
for (int symbol : possibleSymbols) {
doku.getStateManager().pushState();
cellToFill.setSymbolIndex(symbol);
System.out.println("symbol : "+symbol);
System.out.println("doku.isSolved() || Solver.solve(doku) ? "+ (doku.isSolved() || Solver.solve(doku)));
if (doku.isSolved() || Solver.solve(doku)) {
if (Solver.solve(doku)) {
result++;
}
cellToFill.setSymbolIndex(Cell.NOSYMBOL);
doku.getStateManager().popState();
}
return result;
@@ -116,7 +111,7 @@ public class Solver {
return false;
}
List<Integer> possibleSymbols = doku.getPossibleSymbolsOfCell(cellToFill);
List<Integer> possibleSymbols = cellToFill.getPossibleSymbols();
if (possibleSymbols.isEmpty()) {
return false;
}
@@ -156,7 +151,7 @@ public class Solver {
boolean blocked = true;
for (Cell cellToFill : cellsToFill) {
List<Integer> possibleSymbols = doku.getPossibleSymbolsOfCell(cellToFill);
List<Integer> possibleSymbols = cellToFill.getPossibleSymbols();
if (possibleSymbols.size() != 1) {
continue;
}

View File

@@ -24,9 +24,8 @@ public class StupidSolver {
if (!sudoku.getCell(index).isMutable())
return solve(sudoku, index + 1);
Coordinate coords = sudoku.toCoords(index);
for (int symbol = 0; symbol < sudoku.getSize(); symbol++) {
if (sudoku.tryPlaceCellSymbol(coords.getX(), coords.getY(), symbol)) {
if (sudoku.getCell(index).trySetValue(symbol)) {
// on tente de placer sur la case suivante
if (solve(sudoku, index + 1)) {
return true;
@@ -34,7 +33,7 @@ public class StupidSolver {
}
}
// on a tout essayé et rien n'a fonctionné
sudoku.clearCell(coords.getX(), coords.getY());
sudoku.getCell(index).empty();
return false;
}

View File

@@ -24,28 +24,21 @@ public class Cell {
* Il est initialisé à Cell.NOSYMBOL.
*/
private int symbolIndex = Cell.NOSYMBOL;
/**
* Liste des index de symbole possibles pour cette Cell,
* en fonction des contraintes de sudoku dans lequel elle est.
*/
private final List<Integer> possibleSymbols;
/**
* Si cette Cell peut être modififié ou non.
*/
private boolean isMutable = true;
public Cell() {
this.possibleSymbols = new ArrayList<>();
this(Cell.NOSYMBOL);
}
public Cell(int symbolIndex) {
this.symbolIndex = symbolIndex;
this.possibleSymbols = new ArrayList<>();
}
public Cell(int symbolIndex, boolean isMutable) {
this.symbolIndex = symbolIndex;
this.possibleSymbols = new ArrayList<>();
this.isMutable = isMutable;
}
@@ -57,11 +50,6 @@ public class Cell {
this.symbolIndex = symbolIndex;
}
public void setPossibleSymbols(List<Integer> possibleSymbols) {
this.possibleSymbols.clear();
this.possibleSymbols.addAll(possibleSymbols);
}
/**
* Rend la Cell immuable.
*/
@@ -95,14 +83,6 @@ public class Cell {
return this.symbolIndex == Cell.NOSYMBOL;
}
public void removeSymbolFromPossibilities(int indexSymbol) {
possibleSymbols.remove(indexSymbol);
}
public List<Integer> getPossibleSymbols() {
return this.possibleSymbols;
}
/**
* Renvoie si la Cell est modifiable
* @return boolean, true si elle est modifiable ou false sinon.
@@ -120,4 +100,35 @@ public class Cell {
this.symbolIndex = Cell.NOSYMBOL;
return oldSymbol;
}
public boolean canHaveValue(int value) {
for (Sudoku s :getBlock().getSudokus()) {
int cellIndex = s.getCells().indexOf(this);
// la cellule existe
if (cellIndex != -1) {
int cellX = cellIndex % s.getSize();
int cellY = cellIndex / s.getSize();
if (!s.canBePlaced(cellX, cellY, value)) {
return false;
}
}
}
return true;
}
public List<Integer> getPossibleSymbols() {
List<Integer> result = new ArrayList<>();
for (int i = 0; i < getBlock().getSudokus().get(0).getSize(); i++) {
if (canHaveValue(i))
result.add(i);
}
return result;
}
public boolean trySetValue(int newValue) {
if (!canHaveValue(newValue))
return false;
setSymbolIndex(newValue);
return true;
}
}

View File

@@ -19,90 +19,60 @@ public class MultiDoku {
*/
private final List<Sudoku> subGrids;
private final StateManager stateManager;
public MultiDoku(List<Sudoku> subGrids) {
this.subGrids = subGrids;
}
public MultiDoku clone() {
//TODO: ahhhhhhhhhhhhhhhhhhhhhhh
return SudokuSerializer.deserializeSudoku(SudokuSerializer.serializeSudoku(this));
this.stateManager = new StateManager(this);
}
/**
* Renvoie le nombre de sudoku contenu dans ce MultiDoku.
*
* @return int
*/
public int getNbSubGrids(){
public int getNbSubGrids() {
return subGrids.size();
}
/**
* Renvoie la ie sudoku contenue dans ce MultiDoku.
*
* @param i int, indice du sudoku à renvoyer.
* @return Sudoku, ie Sudoku
*/
public Sudoku getSubGrid(int i){
public Sudoku getSubGrid(int i) {
return subGrids.get(i);
}
/**
* Renvoie la liste des Cells contenue dans ce MultiDoku,
* soit les Cells contenues de chaques sous-Sudoku.
*
* @return List<Cell>
*/
public List<Cell> getCells(){
public List<Cell> getCells() {
Set<Cell> cellsSet = new HashSet<>();
for (Sudoku sudoku : subGrids){
for (Sudoku sudoku : subGrids) {
cellsSet.addAll(sudoku.getCells());
}
return new ArrayList<>(cellsSet);
}
/**
* Met à jour les symboles possibles de chaque Cell.
* @throws Exception, si ce n'est pas possible.
*/
public void updateSymbolsPossibilities() throws Exception {
for (Sudoku sudoku : subGrids){
sudoku.updateSymbolsPossibilities();
}
return new ArrayList<>(cellsSet);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Multidoku {");
for (Sudoku sudoku : subGrids){
for (Sudoku sudoku : subGrids) {
sb.append("\n\t").append(sudoku.toString());
}
sb.append("\n}");
return sb.toString();
}
/**
* Renvoie les symboles possibles d'une Cell donnée.
* @param cellToFill Cell.
* @return List<Integer>, liste des symboles possible.
*/
public List<Integer> getPossibleSymbolsOfCell(Cell cellToFill) {
List<Integer> result = new ArrayList<>();
boolean hasBeenFill = false;
for (Sudoku sudoku : this.subGrids) {
if (sudoku.contains(cellToFill)) {
if (!hasBeenFill) {
result.addAll(sudoku.getPossibleSymbolsOfCell(cellToFill));
hasBeenFill = true;
} else {
result.retainAll(sudoku.getPossibleSymbolsOfCell(cellToFill));
}
}
}
return result;
}
/**
* Renvoie les sous-Sudoku
*
* @return List<Sudoku>
*/
public List<Sudoku> getSubGrids() {
@@ -111,43 +81,26 @@ public class MultiDoku {
/**
* Check si le MultiDoku est valide, en fonction de ses sous-Sudokus.
*
* @return boolean, true s'il est valide et false sinon.
*/
public boolean isSolved() {
boolean result = true;
for (Sudoku sudoku : this.subGrids) {
result = sudoku.isSolved() && result;
}
return result;
}
@Override
public boolean equals(Object object) {
if (!(object instanceof MultiDoku)) {
return false;
}
if (this.getNbSubGrids() != ((MultiDoku) object).getNbSubGrids()) {
return false;
}
for (int i = 0; i < this.getNbSubGrids(); i++) {
if (!this.getSubGrid(i).equals(((MultiDoku) object).getSubGrid(i))) {
if (!sudoku.isSolved())
return false;
}
}
return true;
}
/**
* Renvoie la 1re Cell vide des sous-Sudoku.
*
* @return Cell, une Cell vide, ou null s'il n'y en a pas.
*/
public Cell getFirstEmptyCell() {
for (Sudoku sudoku : this.subGrids) {
Cell cellTmp = sudoku.getFirstEmptyCell();
if (cellTmp != null && cellTmp.isEmpty()) {
if (cellTmp != null) {
return cellTmp;
}
}
@@ -156,11 +109,12 @@ public class MultiDoku {
/**
* Renvoie la liste des Cells préalablement remplies du MultiDoku.
*
* @return List<Cell>, vide si aucune Cell n'est remplie.
*/
public List<Cell> getFilledCells() {
List<Cell> result = new ArrayList<>();
for (Cell cell : this.getCells()){
for (Cell cell : this.getCells()) {
if (!cell.isEmpty()) {
result.add(cell);
}
@@ -170,11 +124,12 @@ public class MultiDoku {
/**
* Renvoie la liste des Cells vides du MultiDoku.
*
* @return List<Cell>, vide si aucune Cell ne l'est.
*/
public List<Cell> getEmptyCells() {
List<Cell> result = new ArrayList<>();
for (Cell cell : this.getCells()){
for (Cell cell : this.getCells()) {
if (cell.isEmpty()) {
result.add(cell);
}
@@ -182,9 +137,9 @@ public class MultiDoku {
return result;
}
/**
* Vide une Cell donnée.
*
* @param cell Cell, à vider.
*/
public void empty(Cell cell) {
@@ -195,6 +150,7 @@ public class MultiDoku {
/**
* Renvoie le nombre de Cell contenue dans le MultiDoku.
*
* @return int, nombre de Cell dans le MultiDoku.
*/
public int getNbCells() {
@@ -209,5 +165,9 @@ public class MultiDoku {
filledCell.setImmutable();
}
}
}
public StateManager getStateManager() {
return stateManager;
}
}

View File

@@ -0,0 +1,41 @@
package sudoku.structure;
import java.util.HashMap;
import java.util.Map;
import java.util.Stack;
//TODO: doc
public class StateManager {
private final Stack<Map<Cell, Integer>> states;
private final MultiDoku doku;
public StateManager(MultiDoku doku) {
this.states = new Stack<>();
this.doku = doku;
}
public void pushState() {
states.add(new HashMap<>());
saveState();
}
public void popState() {
assert (states.size() > 0);
restoreState();
states.pop();
}
private void restoreState() {
for (var entry : this.states.getLast().entrySet()) {
entry.getKey().setSymbolIndex(entry.getValue());
}
}
private void saveState() {
for (Cell cell : this.doku.getCells()) {
states.getLast().put(cell, cell.getSymbolIndex());
}
}
}

View File

@@ -3,6 +3,7 @@ package sudoku.structure;
import sudoku.constraint.BlockConstraint;
import sudoku.constraint.Constraint;
import sudoku.constraint.IConstraint;
import sudoku.io.SudokuPrinter;
import java.util.ArrayList;
import java.util.List;
@@ -89,22 +90,6 @@ public class Sudoku {
return true;
}
/**
* 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 value int, index du symbole que l'on veut placer.
* @return boolean, true si le symbole a été placé, false sinon
*/
public boolean tryPlaceCellSymbol(int x, int y, int value) {
assert (isValidCoords(x, y));
if (!canBePlaced(x, y, value))
return false;
Cell cell = getCell(x, y);
cell.setSymbolIndex(value);
return true;
}
/**
* Vide la Cell dotn les coordonnées sont renseignées de son symbole.
* @param x int, abscisse de la Cell voulue.
@@ -194,7 +179,7 @@ public class Sudoku {
}
public Cell getCell(int x, int y) {
int index = y * getSize() + x;
int index = toIndex(x, y);
assert (isValidCoords(x, y));
try {
return this.cells.get(index);
@@ -232,62 +217,6 @@ public class Sudoku {
return this.cells.contains(cell);
}
/**
* Localise la Cell dans le Sudoku.
* @param c Cell, cellule dont on veut les coordonées.
* @return Coordinate, coordonnées de la Cell.
* @throws Exception si la Cell n'appartient pas au Sudoku.
*/
private Coordinate getCoordinateCell(Cell c) throws Exception {
int x = 0, y = 0;
int size = this.getSize();
if (!this.contains(c)) {
throw new Exception("The given cell is not in this sudoku.");
}
// TODO: use this.cells.indexOf();
for (Cell cell : this.cells) {
if (cell == c) {
return new Coordinate(x, y);
}
if (x == size - 1) {
y += 1;
x = 0;
} else {
x += 1;
}
}
return new Coordinate(x, y);
}
/**
* Met à jour les symboles possibles des Cells du Sudoku.
*
*/
public void updateSymbolsPossibilities() {
for (Constraint constraint : constraints) {
List<Cell> cells = this.getCells();
for (Cell cell : cells) {
Coordinate coord = null;
try {
coord = this.getCoordinateCell(cell);
} catch (Exception e) {
System.out.println("Cas jamais atteint.");
}
List<Integer> newPossibleSymbols = cell.getPossibleSymbols();
newPossibleSymbols.retainAll(constraint.getPossibleSymbols(
this,
coord.getX(),
coord.getY()
));
cell.setPossibleSymbols(newPossibleSymbols);
}
}
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Sudoku {");
@@ -315,30 +244,6 @@ public class Sudoku {
return null;
}
/**
* Renvoie l'index des symboles possibles de la Cell passée en paramètres.
* @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.
*/
public List<Integer> getPossibleSymbolsOfCell(Cell cellToFill) {
List<Integer> result = new ArrayList<>();
Coordinate cellCoordinates;
try {
cellCoordinates = this.getCoordinateCell(cellToFill);
} catch (Exception e) {
return result;
}
for (int i = 0; i < this.constraints.size(); i++) {
Constraint constraint = this.constraints.get(i);
if (i == 0) {
result.addAll(constraint.getPossibleSymbols(this, cellCoordinates.getX(), cellCoordinates.getY()));
} else {
result.retainAll(constraint.getPossibleSymbols(this, cellCoordinates.getX(), cellCoordinates.getY()));
}
}
return result;
}
/**
* Vérifie si le Sudoku est résolue, soit complet et cohérent avec ses contraintes.
* @return boolean, valant true si le Sudoku est résolu, false sinon.
@@ -362,89 +267,20 @@ public class Sudoku {
* @return bollean, true si le Sudoku est valide, false sinon
*/
private boolean isValid() {
for (Cell cell : this.getFilledCells()) {
for (Constraint constraint : this.constraints) {
try {
Coordinate coords = this.getCoordinateCell(cell);
for (int i = 0; i < cells.size(); i++) {
Cell cell = getCell(i);
if (cell.isEmpty())
continue;
int symbolPlaced = cell.empty();
List<Integer> possibleSymbols = constraint.getPossibleSymbols(
this,
coords.getX(),
coords.getY()
);
Coordinate coordinate = toCoords(i);
cell.setSymbolIndex(symbolPlaced);
if (!possibleSymbols.contains(symbolPlaced)) {
return false;
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
return true;
}
/**
* Renvoie la liste des Cells remplies.
* @return List<Cell>
*/
private List<Cell> getFilledCells() {
List<Cell> result = new ArrayList<>();
for (Cell cell : getCells()) {
if (!cell.isEmpty()) {
result.add(cell);
}
}
return result;
}
/**
* Renvoie la liste des Cells modifiables.
* @return List<Cell>
*/
private List<Cell> getEmptyCells() {
List<Cell> result = new ArrayList<>();
for (Cell cell : getCells()) {
if (cell.isMutable()) {
result.add(cell);
}
}
return result;
}
/**
* Renvoie la liste des Cells immuables.
* @return List<Cell>
*/
private List<Cell> getImmutableCells() {
List<Cell> result = new ArrayList<>();
for (Cell cell : getCells()) {
if (!cell.isMutable()) {
result.add(cell);
}
}
return result;
}
@Override
public boolean equals(Object object) {
if (!(object instanceof Sudoku)) {
return false;
}
if (this.getSize() != ((Sudoku) object).getSize()) {
return false;
}
for (int i = 0; i < this.getSize(); i++) {
if (this.getCell(i).getSymbolIndex() != ((Sudoku) object).getCell(i).getSymbolIndex()) {
int symbolPlaced = cell.empty();
if (!canBePlaced(coordinate.getX(), coordinate.getY(), symbolPlaced)) {
cell.setSymbolIndex(symbolPlaced);
return false;
}
cell.setSymbolIndex(symbolPlaced);
}
return true;
}

View File

@@ -123,7 +123,6 @@ public class SudokuFactory {
*/
public static boolean newDokuFromFilledOne(MultiDoku doku, int nbCellsToEmpty) throws Exception {
System.out.println("nbCellsToEmpty : "+nbCellsToEmpty);
if (nbCellsToEmpty >= doku.getCells().size()) {
throw new Exception();
}
@@ -141,7 +140,6 @@ public class SudokuFactory {
int oldSymbol = cellToEmpty.empty();
int nbDokuSultions = Solver.countSolution(doku);
System.out.println("oldSymbol : "+oldSymbol);
if (nbDokuSultions == 1) {
if (newDokuFromFilledOne(doku, --nbCellsToEmpty)) {
return true;

View File

@@ -1,5 +1,8 @@
package sudoku;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.File;
import java.util.Random;
@@ -13,24 +16,26 @@ import sudoku.structure.SudokuFactory;
public class SudokuSerializerTest {
void testSerializeWithSize(int blockWidth, int blockHeight, Random r) {
MultiDoku sudoku = SudokuFactory.createBasicEmptyRectangleDoku(blockWidth, blockHeight,
void testSerializeWithSize(int blockWidth, int blockHeight) {
var sudoku = SudokuFactory.createBasicEmptyRectangleDoku(blockWidth, blockHeight,
SudokuFactory.DEFAULT_CONSTRAINTS);
Solver.solveRandom(sudoku, r);
Solver.solveRandom(sudoku, new Random());
JSONObject data = SudokuSerializer.serializeSudoku(sudoku);
MultiDoku multiDoku = SudokuSerializer.deserializeSudoku(data);
assert (data.toString().equals(SudokuSerializer.serializeSudoku(multiDoku).toString()));
assertTrue(data.toString().equals(SudokuSerializer.serializeSudoku(multiDoku).toString()));
}
void testSaveWithSize(int blockWidth, int blockHeight, Random r) {
void testSaveWithSize(int blockWidth, int blockHeight) {
MultiDoku doku = SudokuFactory.createBasicEmptyRectangleDoku(blockWidth, blockHeight,
SudokuFactory.DEFAULT_CONSTRAINTS);
Solver.solveRandom(doku, r);
Solver.solveRandom(doku, new Random());
String savePath = SudokuSerializer.saveMultiDoku(doku);
MultiDoku otherDoku = null;
try {
otherDoku = SudokuFactory.fromfile(savePath);
assert (otherDoku != null);
assertEquals(SudokuSerializer.serializeSudoku(doku).toString(), SudokuSerializer.serializeSudoku(otherDoku).toString());
// clean file after test
File fileToDelete = new File(savePath);
fileToDelete.delete();
@@ -38,27 +43,35 @@ public class SudokuSerializerTest {
e.printStackTrace();
assert false;
}
assert (doku.equals(otherDoku));
}
void testSerializeX(int size) {
var sudoku = SudokuFactory.createBasicXShapedMultidoku(size, SudokuFactory.DEFAULT_CONSTRAINTS);
Solver.solveRandom(sudoku, new Random());
JSONObject data = SudokuSerializer.serializeSudoku(sudoku);
MultiDoku multiDoku = SudokuSerializer.deserializeSudoku(data);
assertTrue(data.toString().equals(SudokuSerializer.serializeSudoku(multiDoku).toString()));
}
@Test
void testSerialize() {
Random r = new Random();
testSerializeWithSize(3, 3, r);
/**
int testCount = 5;
int testCount = 20;
for (int i = 0; i < testCount; i++) {
int blockWidth = r.nextInt(10) + 1;
int blockHeight = r.nextInt(10) + 1;
testSerializeWithSize(blockWidth, blockHeight, r);
int blockWidth = r.nextInt(4) + 1;
int blockHeight = r.nextInt(4) + 1;
testSerializeWithSize(blockWidth, blockHeight);
}
for (int i = 0; i < testCount; i++) {
int blockWidth = r.nextInt(10) + 1;
int blockHeight = r.nextInt(10) + 1;
testSaveWithSize(blockWidth, blockHeight, r);
int blockWidth = r.nextInt(4) + 1;
int blockHeight = r.nextInt(4) + 1;
testSaveWithSize(blockWidth, blockHeight);
}
for (int i = 0; i < testCount; i++) {
int size = r.nextInt(2) + 2;
testSerializeX(size);
}
*/
}
}

View File

@@ -2,90 +2,101 @@ package sudoku.solver;
import org.junit.jupiter.api.Test;
import sudoku.io.SudokuPrinter;
import sudoku.io.SudokuSerializer;
import sudoku.structure.Cell;
import sudoku.structure.MultiDoku;
import sudoku.structure.Sudoku;
import sudoku.structure.SudokuFactory;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.List;
import java.util.Random;
class SolverTest {
@Test
void solveTest() {
Random rand = new Random();
@Test
void solveTest() {
Random rand = new Random();
MultiDoku dokuToTest = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
MultiDoku dokuResult = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
MultiDoku dokuToTest = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
MultiDoku dokuResult = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
Sudoku sudokuToTest = dokuToTest.getSubGrid(0);
Sudoku sudokuResult = dokuResult.getSubGrid(0);
Sudoku sudokuToTest = dokuToTest.getSubGrid(0);
Sudoku sudokuResult = dokuResult.getSubGrid(0);
int ns = Cell.NOSYMBOL;
List<Integer> immutableCells = List.of(ns, ns, 0, ns, ns, 2, 8, ns, 1,
ns, 3, ns, ns, 5, 6, 7, ns, ns,
ns, ns, ns, 8, ns, 7, ns, ns, 6,
0, ns, 1, ns, ns, ns, ns, ns, ns,
4, 8, 7, 5, 1, ns, 6, ns, ns,
6, ns, 3, 2, ns, ns, ns, 8, 0,
ns, ns, 6, ns, ns, 8, ns, 7, 5,
8, 0, ns, 7, ns, 5, 2, ns, 3,
5, ns, ns, ns, 3, 1, 0, ns, ns);
int ns = Cell.NOSYMBOL;
List<Integer> immutableCells = List.of(ns, ns, 0, ns, ns, 2, 8, ns, 1,
ns, 3, ns, ns, 5, 6, 7, ns, ns,
ns, ns, ns, 8, ns, 7, ns, ns, 6,
0, ns, 1, ns, ns, ns, ns, ns, ns,
4, 8, 7, 5, 1, ns, 6, ns, ns,
6, ns, 3, 2, ns, ns, ns, 8, 0,
ns, ns, 6, ns, ns, 8, ns, 7, 5,
8, 0, ns, 7, ns, 5, 2, ns, 3,
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,
2, 3, 8, 1, 5, 6, 7, 0, 4,
1, 4, 5, 8, 0, 7, 3, 2, 6,
0, 2, 1, 6, 8, 3, 5, 4, 7,
4, 8, 7, 5, 1, 0, 6, 3, 2,
6, 5, 3, 2, 7, 4, 1, 8, 0,
3, 1, 6, 0, 2, 8, 4, 7, 5,
8, 0, 4, 7, 6, 5, 2, 1, 3,
5, 7, 2, 4, 3, 1, 0, 6, 8);
List<Integer> correctCells = List.of(7, 6, 0, 3, 4, 2, 8, 5, 1,
2, 3, 8, 1, 5, 6, 7, 0, 4,
1, 4, 5, 8, 0, 7, 3, 2, 6,
0, 2, 1, 6, 8, 3, 5, 4, 7,
4, 8, 7, 5, 1, 0, 6, 3, 2,
6, 5, 3, 2, 7, 4, 1, 8, 0,
3, 1, 6, 0, 2, 8, 4, 7, 5,
8, 0, 4, 7, 6, 5, 2, 1, 3,
5, 7, 2, 4, 3, 1, 0, 6, 8);
sudokuResult.setCellsSymbol(correctCells);
sudokuResult.setCellsSymbol(correctCells);
System.out.println("\n****************************Doku Control\n");
SudokuPrinter.printRectangleSudoku(sudokuResult, 3, 3);
System.out.println("\n****************************Doku Control\n");
SudokuPrinter.printRectangleSudoku(sudokuResult, 3, 3);
assert(dokuResult.isSolved());
assert (dokuResult.isSolved());
Solver.solveRandom(dokuToTest, rand);
Solver.solveRandom(dokuToTest, rand);
System.out.println("\n****************************\nDoku solved");
SudokuPrinter.printRectangleSudoku(dokuToTest.getSubGrid(0), 3, 3);
System.out.println("\n****************************\nDoku solved");
SudokuPrinter.printRectangleSudoku(dokuToTest.getSubGrid(0), 3, 3);
assert (dokuToTest.isSolved());
assert(dokuToTest.isSolved());
for (Cell cell : sudokuToTest.getCells()) {
cell.setImmutable();
}
assert(dokuToTest.equals(dokuResult));
for (Cell cell : sudokuResult.getCells()) {
cell.setImmutable();
}
MultiDoku dokuToTest2 = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
Sudoku sudokuToTest2 = dokuToTest2.getSubGrid(0);
assertEquals(SudokuSerializer.serializeSudoku(dokuResult).toString(),
SudokuSerializer.serializeSudoku(dokuToTest).toString());
List<Integer> immutableCells2 = List.of(ns, ns, 0, ns, ns, 2, 8, ns, 1,
1, 3, ns, ns, 5, 6, 7, ns, ns,
ns, ns, ns, 8, ns, 7, ns, ns, 6,
0, ns, 1, ns, ns, ns, ns, ns, ns,
4, 8, 7, 5, 1, ns, 6, ns, ns,
6, ns, 3, 2, ns, ns, ns, 8, 0,
ns, ns, 6, ns, ns, 8, ns, 7, 5,
8, 0, ns, 7, ns, 5, 2, ns, 3,
5, ns, ns, ns, 3, 1, 0, ns, ns);
sudokuToTest2.setImmutableCellsSymbol(immutableCells2);
MultiDoku dokuToTest2 = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
Sudoku sudokuToTest2 = dokuToTest2.getSubGrid(0);
boolean isSolved = Solver.solveRandom(dokuToTest2, rand);
List<Integer> immutableCells2 = List.of(ns, ns, 0, ns, ns, 2, 8, ns, 1,
1, 3, ns, ns, 5, 6, 7, ns, ns,
ns, ns, ns, 8, ns, 7, ns, ns, 6,
0, ns, 1, ns, ns, ns, ns, ns, ns,
4, 8, 7, 5, 1, ns, 6, ns, ns,
6, ns, 3, 2, ns, ns, ns, 8, 0,
ns, ns, 6, ns, ns, 8, ns, 7, 5,
8, 0, ns, 7, ns, 5, 2, ns, 3,
5, ns, ns, ns, 3, 1, 0, ns, ns);
sudokuToTest2.setImmutableCellsSymbol(immutableCells2);
assert (!isSolved);
boolean isSolved = Solver.solveRandom(dokuToTest2, rand);
MultiDoku dokuToTest3 = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
assert (!isSolved);
Solver.solveRandom(dokuToTest3, rand);
MultiDoku dokuToTest3 = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
SudokuPrinter.printRectangleSudoku(dokuToTest3.getSubGrid(0), 3, 3);
}
Solver.solveRandom(dokuToTest3, rand);
SudokuPrinter.printRectangleSudoku(dokuToTest3.getSubGrid(0), 3, 3);
}
}