Compare commits
12 Commits
4a8644181a
...
tests
| Author | SHA1 | Date | |
|---|---|---|---|
| abf6f6c7c3 | |||
|
|
627c49b961 | ||
|
|
b4157167b5 | ||
|
|
1419898955 | ||
|
|
dd3be67be0 | ||
|
|
c6a32b8d07 | ||
|
|
a8439df736 | ||
|
|
27032d264d | ||
|
|
8afd51b353 | ||
| 2460b7539e | |||
| 0f5020b0b4 | |||
| ac4f859fd8 |
2
.github/workflows/build.yml
vendored
2
.github/workflows/build.yml
vendored
@@ -19,7 +19,7 @@ jobs:
|
|||||||
uses: gradle/actions/setup-gradle@v4
|
uses: gradle/actions/setup-gradle@v4
|
||||||
|
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
run: ./gradlew build
|
run: ./gradlew assemble
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: ./gradlew test
|
run: ./gradlew test
|
||||||
|
|||||||
@@ -18,9 +18,8 @@ repositories {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
// Use JUnit Jupiter for testing.
|
// Use JUnit Jupiter for testing.
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.1'
|
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1'
|
||||||
|
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
|
||||||
implementation 'com.google.guava:guava:31.1-jre'
|
|
||||||
|
|
||||||
implementation 'org.json:json:20250107'
|
implementation 'org.json:json:20250107'
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
package sudoku.io;
|
package sudoku.io;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import gui.RenderableMultidoku;
|
import gui.RenderableMultidoku;
|
||||||
import gui.constants.Symbols;
|
import gui.constants.Symbols;
|
||||||
import sudoku.structure.Cell;
|
import sudoku.structure.Cell;
|
||||||
import sudoku.structure.MultiDoku;
|
import sudoku.structure.MultiDoku;
|
||||||
import sudoku.structure.Sudoku;
|
import sudoku.structure.Sudoku;
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class SudokuPrinter {
|
public class SudokuPrinter {
|
||||||
public static final String ANSI_RESET = "\u001B[0m";
|
public static final String ANSI_RESET = "\u001B[0m";
|
||||||
public static final String ANSI_RED = "\u001B[31m";
|
public static final String ANSI_RED = "\u001B[31m";
|
||||||
@@ -47,7 +47,7 @@ public class SudokuPrinter {
|
|||||||
List<String> listSymbols) {
|
List<String> listSymbols) {
|
||||||
StringBuilder header = new StringBuilder("");
|
StringBuilder header = new StringBuilder("");
|
||||||
header.append(" ");
|
header.append(" ");
|
||||||
for (int x = 0; x < blockWidth*blockHeight; x++) {
|
for (int x = 0; x < blockWidth * blockHeight; x++) {
|
||||||
header.append(x + 1).append(" ");
|
header.append(x + 1).append(" ");
|
||||||
if (x % blockWidth == blockWidth - 1 && x != blockWidth * blockHeight - 1) {
|
if (x % blockWidth == blockWidth - 1 && x != blockWidth * blockHeight - 1) {
|
||||||
header.append(" ");
|
header.append(" ");
|
||||||
@@ -77,11 +77,12 @@ public class SudokuPrinter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void printMultiDoku(final RenderableMultidoku rm, Symbols symbols, int blockWidth, int blockHeight) {
|
public static String printMultiDoku(final RenderableMultidoku rm, Symbols symbols, int blockWidth,
|
||||||
printMultiDoku(rm, symbols.getSymbols(), blockWidth, blockHeight);
|
int blockHeight) {
|
||||||
|
return printMultiDoku(rm, symbols.getSymbols(), blockWidth, blockHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void printMultiDoku(final RenderableMultidoku rm, List<String> listSymbols, int blockWidth,
|
public static String printMultiDoku(final RenderableMultidoku rm, List<String> listSymbols, int blockWidth,
|
||||||
int blockHeight) {
|
int blockHeight) {
|
||||||
StringBuilder line = new StringBuilder("\n");
|
StringBuilder line = new StringBuilder("\n");
|
||||||
int nBlockInWidth = rm.getWidth() / blockWidth;
|
int nBlockInWidth = rm.getWidth() / blockWidth;
|
||||||
@@ -108,7 +109,8 @@ public class SudokuPrinter {
|
|||||||
line.append("]\n");
|
line.append("]\n");
|
||||||
}
|
}
|
||||||
line.append("__".repeat(Math.max(0, rm.getWidth() + nBlockInWidth))).append("_\n");
|
line.append("__".repeat(Math.max(0, rm.getWidth() + nBlockInWidth))).append("_\n");
|
||||||
System.out.println(line);
|
// System.out.println(line);
|
||||||
|
return line.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void printMultiDokuWithIndex(final RenderableMultidoku rm, List<String> listSymbols, int blockWidth,
|
public static void printMultiDokuWithIndex(final RenderableMultidoku rm, List<String> listSymbols, int blockWidth,
|
||||||
@@ -127,7 +129,7 @@ public class SudokuPrinter {
|
|||||||
if (y % blockHeight == 0) {
|
if (y % blockHeight == 0) {
|
||||||
line.append(" ").append("__".repeat(Math.max(0, rm.getWidth() + nBlockInWidth))).append("_\n");
|
line.append(" ").append("__".repeat(Math.max(0, rm.getWidth() + nBlockInWidth))).append("_\n");
|
||||||
}
|
}
|
||||||
line.append(y+1).append(" [ ");
|
line.append(y + 1).append(" [ ");
|
||||||
for (int x = 0; x < rm.getWidth(); x++) {
|
for (int x = 0; x < rm.getWidth(); x++) {
|
||||||
if (x % blockWidth == 0 && x > 0) {
|
if (x % blockWidth == 0 && x > 0) {
|
||||||
line.append("| ");
|
line.append("| ");
|
||||||
@@ -182,12 +184,16 @@ public class SudokuPrinter {
|
|||||||
return result.toString();
|
return result.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void printMultiDoku(final MultiDoku doku, int blockWidth, int blockHeight, Symbols symbols) {
|
public static String printMultiDoku(final MultiDoku doku) {
|
||||||
if (doku.getNbSubGrids() == 1) {
|
int blockWidth = doku.getSubGrid(0).getBlockWidth();
|
||||||
printRectangleSudoku(doku.getSubGrid(0), blockWidth, blockHeight, symbols);
|
if (blockWidth == 0)
|
||||||
} else {
|
return printMultiDoku(doku, 0, 0, Symbols.Numbers);
|
||||||
printMultiDoku(RenderableMultidoku.fromMultidoku(doku), symbols, blockWidth, blockHeight);
|
else
|
||||||
}
|
return printMultiDoku(doku, blockWidth, doku.getSubGrid(0).getSize() / blockWidth, Symbols.Letters);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String printMultiDoku(final MultiDoku doku, int blockWidth, int blockHeight, Symbols symbols) {
|
||||||
|
return printMultiDoku(RenderableMultidoku.fromMultidoku(doku), symbols, blockWidth, blockHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void printMultiDokuWithIndex(final MultiDoku doku, int blockWidth, int blockHeight, Symbols symbols) {
|
public static void printMultiDokuWithIndex(final MultiDoku doku, int blockWidth, int blockHeight, Symbols symbols) {
|
||||||
|
|||||||
@@ -9,11 +9,10 @@ import java.util.Random;
|
|||||||
import sudoku.structure.Cell;
|
import sudoku.structure.Cell;
|
||||||
import sudoku.structure.MultiDoku;
|
import sudoku.structure.MultiDoku;
|
||||||
|
|
||||||
|
//TODO
|
||||||
public class HintHelper {
|
public class HintHelper {
|
||||||
|
|
||||||
public static record Hint(Cell cell, int newValue) {
|
public static record Hint(Cell cell, int newValue) {}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Hint getHint(MultiDoku doku, Solver solver) {
|
public static Hint getHint(MultiDoku doku, Solver solver) {
|
||||||
doku.getStateManager().pushState();
|
doku.getStateManager().pushState();
|
||||||
@@ -36,7 +35,7 @@ public class HintHelper {
|
|||||||
if (newValue == Cell.NOSYMBOL)
|
if (newValue == Cell.NOSYMBOL)
|
||||||
return new Hint(cell, newValue);
|
return new Hint(cell, newValue);
|
||||||
// we have to change the cell value
|
// we have to change the cell value
|
||||||
if (oldValue != Cell.NOSYMBOL && newValue != oldValue)
|
if (oldValue != Cell.NOSYMBOL)
|
||||||
return new Hint(cell, newValue);
|
return new Hint(cell, newValue);
|
||||||
|
|
||||||
// there is a valid move
|
// there is a valid move
|
||||||
|
|||||||
@@ -4,28 +4,41 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
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
|
* Un Block étant un ensemble de cellule avec une contrainte de block qui lui
|
||||||
* ait associé
|
* ait associé.
|
||||||
*/
|
*/
|
||||||
public class Block {
|
public class Block {
|
||||||
|
|
||||||
|
// <editor-fold defaultstate="collapsed" desc="ATTRIBUTS">
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* L'ensemble des cellules du block
|
* L'ensemble des cellules du block.
|
||||||
*/
|
*/
|
||||||
private final List<Cell> cells;
|
private final List<Cell> cells;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* List de sudoku qui contiennent le block
|
* Liste de Sudoku qui contiennent le block
|
||||||
* Pour un acces plus rapide aux sudokus
|
* Pour un accès plus rapide aux sudokus.
|
||||||
*/
|
*/
|
||||||
private final List<Sudoku> sudokus;
|
private final List<Sudoku> sudokus;
|
||||||
|
|
||||||
|
// </editor-fold>
|
||||||
|
|
||||||
|
// <editor-fold defaultstate="collapsed" desc="METHODES">
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un Block avec une liste de Cell donnée.
|
||||||
|
* @param cells List<Cell>
|
||||||
|
*/
|
||||||
public Block(List<Cell> cells) {
|
public Block(List<Cell> cells) {
|
||||||
this.cells = cells;
|
this.cells = cells;
|
||||||
this.sudokus = new ArrayList<>();
|
this.sudokus = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un Block avec une liste de Cell vide.
|
||||||
|
*/
|
||||||
public Block() {
|
public Block() {
|
||||||
this(new ArrayList<>());
|
this(new ArrayList<>());
|
||||||
}
|
}
|
||||||
@@ -33,13 +46,16 @@ public class Block {
|
|||||||
public List<Cell> getCells() {
|
public List<Cell> getCells() {
|
||||||
return cells;
|
return cells;
|
||||||
}
|
}
|
||||||
|
public List<Sudoku> getSudokus() {
|
||||||
|
return sudokus;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ajoute une Cell au Block
|
* Ajoute une Cell au Block
|
||||||
*
|
*
|
||||||
* @param newCell Cell, à ajouter
|
* @param newCell Cell, à ajouter
|
||||||
*/
|
*/
|
||||||
void addCell(Cell newCell) {
|
public void addCell(Cell newCell) {
|
||||||
this.cells.add(newCell);
|
this.cells.add(newCell);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,16 +73,5 @@ public class Block {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean containsCell(Cell cell) {
|
// </editor-fold>
|
||||||
for (Cell cellTmp : this.cells) {
|
|
||||||
if (cellTmp.equals(cell)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Sudoku> getSudokus() {
|
|
||||||
return sudokus;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import java.util.List;
|
|||||||
*/
|
*/
|
||||||
public class Cell {
|
public class Cell {
|
||||||
|
|
||||||
|
// <editor-fold defaultstate="collapsed" desc="ATTRIBUTS">
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constante de valeur d'index de symbole quand il n'y en a pas,
|
* Constante de valeur d'index de symbole quand il n'y en a pas,
|
||||||
* soit que la Cell est vide.
|
* soit que la Cell est vide.
|
||||||
@@ -29,6 +31,10 @@ public class Cell {
|
|||||||
*/
|
*/
|
||||||
private boolean isMutable = true;
|
private boolean isMutable = true;
|
||||||
|
|
||||||
|
// </editor-fold>
|
||||||
|
|
||||||
|
// <editor-fold defaultstate="collapsed" desc="METHODES">
|
||||||
|
|
||||||
public Cell() {
|
public Cell() {
|
||||||
this(Cell.NOSYMBOL);
|
this(Cell.NOSYMBOL);
|
||||||
}
|
}
|
||||||
@@ -46,6 +52,22 @@ public class Cell {
|
|||||||
return this.symbolIndex;
|
return this.symbolIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renvoie le Block qui la contient.
|
||||||
|
* @return Block.
|
||||||
|
*/
|
||||||
|
public Block getBlock() {
|
||||||
|
return this.blockContainer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renvoie si la Cell est modifiable
|
||||||
|
* @return boolean, true si elle est modifiable ou false sinon.
|
||||||
|
*/
|
||||||
|
public boolean isMutable() {
|
||||||
|
return this.isMutable;
|
||||||
|
}
|
||||||
|
|
||||||
public void setSymbolIndex(int symbolIndex) {
|
public void setSymbolIndex(int symbolIndex) {
|
||||||
this.symbolIndex = symbolIndex;
|
this.symbolIndex = symbolIndex;
|
||||||
}
|
}
|
||||||
@@ -57,22 +79,16 @@ public class Cell {
|
|||||||
this.isMutable = false;
|
this.isMutable = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Block getBlock() {
|
|
||||||
return this.blockContainer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBlock(Block block) {
|
public void setBlock(Block block) {
|
||||||
this.blockContainer = block;
|
this.blockContainer = block;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove the current symbolIndex and returns it
|
* Vide la Cell.
|
||||||
* @return integer symbolIndex cleared
|
|
||||||
*/
|
*/
|
||||||
public int clearCurrentSymbol() {
|
public void clearCurrentSymbol() {
|
||||||
int i = this.symbolIndex;
|
|
||||||
setSymbolIndex(NOSYMBOL);
|
setSymbolIndex(NOSYMBOL);
|
||||||
return i;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -83,14 +99,6 @@ public class Cell {
|
|||||||
return this.symbolIndex == Cell.NOSYMBOL;
|
return this.symbolIndex == Cell.NOSYMBOL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Renvoie si la Cell est modifiable
|
|
||||||
* @return boolean, true si elle est modifiable ou false sinon.
|
|
||||||
*/
|
|
||||||
public boolean isMutable() {
|
|
||||||
return this.isMutable;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vide la Cell, en renvoie l'ancien index du symbole qui était dedans.
|
* Vide la Cell, en renvoie l'ancien index du symbole qui était dedans.
|
||||||
* @return int, index du symbole anciennement contenue dans la Cell.
|
* @return int, index du symbole anciennement contenue dans la Cell.
|
||||||
@@ -101,6 +109,11 @@ public class Cell {
|
|||||||
return oldSymbol;
|
return oldSymbol;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vérifie si la Cell peut prendre ce symbole par rapport aux contraintes de ses Sudokus.
|
||||||
|
* @param value int, index du symbole
|
||||||
|
* @return boolean, true si elle peut, false sinon.
|
||||||
|
*/
|
||||||
public boolean canHaveValue(int value) {
|
public boolean canHaveValue(int value) {
|
||||||
for (Sudoku s :getBlock().getSudokus()) {
|
for (Sudoku s :getBlock().getSudokus()) {
|
||||||
int cellIndex = s.getCells().indexOf(this);
|
int cellIndex = s.getCells().indexOf(this);
|
||||||
@@ -116,6 +129,10 @@ public class Cell {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renvoie les symboles que peut prendre cette Cell par rapport aux contraintes de ses Sudokus.
|
||||||
|
* @return List<Integer>, la liste des symboles possibles.
|
||||||
|
*/
|
||||||
public List<Integer> getPossibleSymbols() {
|
public List<Integer> getPossibleSymbols() {
|
||||||
List<Integer> result = new ArrayList<>();
|
List<Integer> result = new ArrayList<>();
|
||||||
for (int i = 0; i < getBlock().getSudokus().get(0).getSize(); i++) {
|
for (int i = 0; i < getBlock().getSudokus().get(0).getSize(); i++) {
|
||||||
@@ -125,6 +142,11 @@ public class Cell {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Essaye de placer la valeur et renvoie false si ce n'est pas possible.
|
||||||
|
* @param newValue int, valeur à placer.
|
||||||
|
* @return boolean, true si la Cell à pris la valeur newValue, false sinon.
|
||||||
|
*/
|
||||||
public boolean trySetValue(int newValue) {
|
public boolean trySetValue(int newValue) {
|
||||||
if (!isMutable())
|
if (!isMutable())
|
||||||
return false;
|
return false;
|
||||||
@@ -133,4 +155,6 @@ public class Cell {
|
|||||||
setSymbolIndex(newValue);
|
setSymbolIndex(newValue);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// </editor-fold>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,12 @@
|
|||||||
package sudoku.structure;
|
package sudoku.structure;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Représente les coordonnées d'une Cell
|
* Représente les coordonnées d'une Cell.
|
||||||
*/
|
*/
|
||||||
public class Coordinate {
|
public class Coordinate {
|
||||||
|
|
||||||
|
// <editor-fold defaultstate="collapsed" desc="ATTRIBUTS">
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* L'abscisse de la Cell.
|
* L'abscisse de la Cell.
|
||||||
*/
|
*/
|
||||||
@@ -14,6 +16,10 @@ public class Coordinate {
|
|||||||
*/
|
*/
|
||||||
private int y;
|
private int y;
|
||||||
|
|
||||||
|
// </editor-fold>
|
||||||
|
|
||||||
|
// <editor-fold defaultstate="collapsed" desc="METHODES">
|
||||||
|
|
||||||
public Coordinate(int x, int y) {
|
public Coordinate(int x, int y) {
|
||||||
this.x = x;
|
this.x = x;
|
||||||
this.y = y;
|
this.y = y;
|
||||||
@@ -35,16 +41,23 @@ public class Coordinate {
|
|||||||
this.y = y;
|
this.y = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int calculateIndex(int size) {
|
/**
|
||||||
return this.y * size + this.x;
|
* Ajoute la Coordiante donnée à celle-ci.
|
||||||
}
|
* @param other Coordiante, à ajouter.
|
||||||
|
* @return Coordinate, le résultat de l'addition.
|
||||||
|
*/
|
||||||
public Coordinate add(Coordinate other) {
|
public Coordinate add(Coordinate other) {
|
||||||
return new Coordinate(this.x + other.x, this.y + other.y);
|
return new Coordinate(this.x + other.x, this.y + other.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Soustrait la Coordiante donnée à celle-ci.
|
||||||
|
* @param other Coordiante, à soustraire.
|
||||||
|
* @return Coordinate, le résultat de la soustraction.
|
||||||
|
*/
|
||||||
public Coordinate sub(Coordinate other) {
|
public Coordinate sub(Coordinate other) {
|
||||||
return new Coordinate(this.x - other.x, this.y - other.y);
|
return new Coordinate(this.x - other.x, this.y - other.y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// </editor-fold>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,26 +1,32 @@
|
|||||||
package sudoku.structure;
|
package sudoku.structure;
|
||||||
|
|
||||||
//TODO: melvyn va passer par là
|
/**
|
||||||
|
* Les difficultés d'un Sudoku,
|
||||||
|
* représente le nombre de cases à enlever, en proportion, à un Sudoku.
|
||||||
|
* Par exemple, avec 0.1 de factor, on enlevera 10% des cases.
|
||||||
|
*/
|
||||||
public enum Difficulty {
|
public enum Difficulty {
|
||||||
|
|
||||||
VeryEasy("Très facile", 0.1), Easy("Facile", 0.25), Medium("Moyen", 0.5), Hard("Difficile", 0.75), VeryHard("Impossible", 0.78);
|
VeryEasy("Très facile", 0.1),
|
||||||
|
Easy("Facile", 0.25),
|
||||||
|
Medium("Moyen", 0.5),
|
||||||
|
Hard("Difficile", 0.75),
|
||||||
|
VeryHard("Impossible", 0.78);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Le pourcentage de Cell à enlever.
|
||||||
|
*/
|
||||||
final double factor;
|
final double factor;
|
||||||
|
/**
|
||||||
|
* Le nom de la difficulté.
|
||||||
|
*/
|
||||||
final String displayName;
|
final String displayName;
|
||||||
|
|
||||||
private Difficulty(String displayName, double factor) {
|
Difficulty(String displayName, double factor) {
|
||||||
this.factor = factor;
|
this.factor = factor;
|
||||||
this.displayName = displayName;
|
this.displayName = displayName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDisplayName() {
|
|
||||||
return displayName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public double getFactor() {
|
|
||||||
return factor;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final String[] difficultyNames;
|
private static final String[] difficultyNames;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
@@ -31,6 +37,14 @@ public enum Difficulty {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getDisplayName() {
|
||||||
|
return displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getFactor() {
|
||||||
|
return factor;
|
||||||
|
}
|
||||||
|
|
||||||
public static String[] getDifficultyNames() {
|
public static String[] getDifficultyNames() {
|
||||||
return difficultyNames;
|
return difficultyNames;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,100 @@
|
|||||||
package sudoku.structure;
|
package sudoku.structure;
|
||||||
|
|
||||||
|
import sudoku.io.SudokuPrinter;
|
||||||
|
import sudoku.io.SudokuSerializer;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import sudoku.io.SudokuSerializer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @class MultiDoku
|
* @class MultiDoku
|
||||||
* @brief Représente une grille de Multidoku.
|
* @brief Représente une grille de Multidoku.
|
||||||
* Une grille de sudoku est un multidoku avec un seul sous-sudoku
|
* Une grille de sudoku est un multidoku avec un seul sous-sudoku.
|
||||||
*/
|
*/
|
||||||
public class MultiDoku {
|
public class MultiDoku {
|
||||||
|
|
||||||
|
// <editor-fold defaultstate="collapsed" desc="ATTRIBUTS">
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Liste des sous-Sudoku contenue dans le multidoku.
|
* Liste des sous-Sudoku contenue dans le multidoku.
|
||||||
*/
|
*/
|
||||||
private final List<Sudoku> subGrids;
|
private final List<Sudoku> subGrids;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pile, qui contient des états du MultiDoku,
|
||||||
|
* utile pour la résolution.
|
||||||
|
*/
|
||||||
private final StateManager stateManager;
|
private final StateManager stateManager;
|
||||||
|
|
||||||
|
// </editor-fold>
|
||||||
|
|
||||||
|
// <editor-fold defaultstate="collapsed" desc="METHODES">
|
||||||
|
|
||||||
public MultiDoku(List<Sudoku> subGrids) {
|
public MultiDoku(List<Sudoku> subGrids) {
|
||||||
this.subGrids = subGrids;
|
this.subGrids = subGrids;
|
||||||
this.stateManager = new StateManager(this);
|
this.stateManager = new StateManager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check si le MultiDoku est résolu, c'est à dire complet et cohérent avec ses contraintes.
|
||||||
|
*
|
||||||
|
* @return boolean, true s'il est résolu et false sinon.
|
||||||
|
*/
|
||||||
|
public boolean isSolved() {
|
||||||
|
for (Sudoku sudoku : this.subGrids) {
|
||||||
|
if (!sudoku.isSolved())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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()) {
|
||||||
|
if (!cell.isEmpty()) {
|
||||||
|
result.add(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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()) {
|
||||||
|
if (cell.isEmpty()) {
|
||||||
|
result.add(cell);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
|
return cellTmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renvoie le nombre de sudoku contenu dans ce MultiDoku.
|
* Renvoie le nombre de sudoku contenu dans ce MultiDoku.
|
||||||
*
|
*
|
||||||
@@ -60,95 +128,6 @@ public class MultiDoku {
|
|||||||
return new ArrayList<>(cellsSet);
|
return new ArrayList<>(cellsSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
StringBuilder sb = new StringBuilder();
|
|
||||||
sb.append("Multidoku {");
|
|
||||||
for (Sudoku sudoku : subGrids) {
|
|
||||||
sb.append("\n\t").append(sudoku.toString());
|
|
||||||
}
|
|
||||||
sb.append("\n}");
|
|
||||||
return sb.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Renvoie les sous-Sudoku
|
|
||||||
*
|
|
||||||
* @return List<Sudoku>
|
|
||||||
*/
|
|
||||||
public List<Sudoku> getSubGrids() {
|
|
||||||
return this.subGrids;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check si le MultiDoku est résolu, c'est à dire complet et cohérent avec ses contraintes.
|
|
||||||
*
|
|
||||||
* @return boolean, true s'il est résolu et false sinon.
|
|
||||||
*/
|
|
||||||
public boolean isSolved() {
|
|
||||||
for (Sudoku sudoku : this.subGrids) {
|
|
||||||
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) {
|
|
||||||
return cellTmp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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()) {
|
|
||||||
if (!cell.isEmpty()) {
|
|
||||||
result.add(cell);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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()) {
|
|
||||||
if (cell.isEmpty()) {
|
|
||||||
result.add(cell);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Vide une Cell donnée.
|
|
||||||
*
|
|
||||||
* @param cell Cell, à vider.
|
|
||||||
*/
|
|
||||||
public void empty(Cell cell) {
|
|
||||||
List<Cell> cells = getCells();
|
|
||||||
Cell cellToEmpty = cells.get(cells.indexOf(cell));
|
|
||||||
cellToEmpty.setSymbolIndex(Cell.NOSYMBOL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renvoie le nombre de Cell contenue dans le MultiDoku.
|
* Renvoie le nombre de Cell contenue dans le MultiDoku.
|
||||||
*
|
*
|
||||||
@@ -167,22 +146,9 @@ public class MultiDoku {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public StateManager getStateManager() {
|
|
||||||
return stateManager;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Renvoie une Cell vide choisie aléatoirement.
|
* Vide les Cells modifiable.
|
||||||
*
|
|
||||||
* @param rand Random, pour le choix aléatoire.
|
|
||||||
* @return Cell, une Cell vide.
|
|
||||||
*/
|
*/
|
||||||
public Cell getRandomEmptyCell(Random rand) {
|
|
||||||
List<Cell> emptyCells = getEmptyCells();
|
|
||||||
int randomIndex = rand.nextInt(emptyCells.size());
|
|
||||||
return emptyCells.get(randomIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void clearMutableCells() {
|
public void clearMutableCells() {
|
||||||
for (Sudoku s : getSubGrids()) {
|
for (Sudoku s : getSubGrids()) {
|
||||||
for (Cell cell : s.getCells()) {
|
for (Cell cell : s.getCells()) {
|
||||||
@@ -192,8 +158,47 @@ public class MultiDoku {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renvoie les sous-Sudoku
|
||||||
|
*
|
||||||
|
* @return List<Sudoku>
|
||||||
|
*/
|
||||||
|
public List<Sudoku> getSubGrids() {
|
||||||
|
return this.subGrids;
|
||||||
|
}
|
||||||
|
|
||||||
|
public StateManager getStateManager() {
|
||||||
|
return stateManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object other) {
|
||||||
|
if (!(other instanceof MultiDoku otherDoku))
|
||||||
|
return false;
|
||||||
|
if (this.getNbSubGrids() != otherDoku.getNbSubGrids())
|
||||||
|
return false;
|
||||||
|
for (int i = 0; i < this.getNbSubGrids(); i++) {
|
||||||
|
Sudoku sudoku = this.getSubGrid(i);
|
||||||
|
Sudoku otherSudoku = otherDoku.getSubGrid(i);
|
||||||
|
if (sudoku.getSize() != otherSudoku.getSize())
|
||||||
|
return false;
|
||||||
|
for (int j = 0; j < sudoku.getSize() * sudoku.getSize(); j++) {
|
||||||
|
if (sudoku.getCell(j).getSymbolIndex() != otherSudoku.getCell(j).getSymbolIndex())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return SudokuPrinter.printMultiDoku(this);
|
||||||
|
}
|
||||||
|
|
||||||
public MultiDoku clone() {
|
public MultiDoku clone() {
|
||||||
// TODO: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaah
|
// TODO: C'est pas dingue de le faire comme ça...
|
||||||
return SudokuSerializer.deserializeSudoku(SudokuSerializer.serializeSudoku(this));
|
return SudokuSerializer.deserializeSudoku(SudokuSerializer.serializeSudoku(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// </editor-fold>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,22 @@ import java.util.HashMap;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
//TODO: doc
|
/**
|
||||||
|
* Une pile, qui réprésente les états d'un MultiDoku,
|
||||||
|
* utile pour les Solvers.
|
||||||
|
*/
|
||||||
public class StateManager {
|
public class StateManager {
|
||||||
|
|
||||||
private final Stack<Map<Cell, Integer>> states;
|
/**
|
||||||
|
* Le MultiDoku auquel il est associé.
|
||||||
|
*/
|
||||||
private final MultiDoku doku;
|
private final MultiDoku doku;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* La pile des états du MultiDoku associé.
|
||||||
|
*/
|
||||||
|
private final Stack<Map<Cell, Integer>> states;
|
||||||
|
|
||||||
public StateManager(MultiDoku doku) {
|
public StateManager(MultiDoku doku) {
|
||||||
this.states = new Stack<>();
|
this.states = new Stack<>();
|
||||||
this.doku = doku;
|
this.doku = doku;
|
||||||
@@ -21,12 +31,12 @@ public class StateManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void popState() {
|
public void popState() {
|
||||||
assert (states.size() > 0);
|
assert (!states.isEmpty());
|
||||||
restoreState(states.pop());
|
restoreState(states.pop());
|
||||||
}
|
}
|
||||||
|
|
||||||
public Map<Cell, Integer> popAndGetState() {
|
public Map<Cell, Integer> popAndGetState() {
|
||||||
assert (states.size() > 0);
|
assert (!states.isEmpty());
|
||||||
var currentState = saveState();
|
var currentState = saveState();
|
||||||
restoreState(states.pop());
|
restoreState(states.pop());
|
||||||
return currentState;
|
return currentState;
|
||||||
|
|||||||
@@ -12,24 +12,33 @@ import sudoku.constraint.IConstraint;
|
|||||||
*/
|
*/
|
||||||
public class Sudoku {
|
public class Sudoku {
|
||||||
|
|
||||||
|
// <editor-fold defaultstate="collapsed" desc="ATTRIBUTS">
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Liste des Block contenus dans le Sudoku.
|
* Liste des Block contenus dans le Sudoku.
|
||||||
*/
|
*/
|
||||||
private final List<Block> blocks;
|
private final List<Block> blocks;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Liste des Cells contenus dans le Sudoku.
|
* Liste des Cells contenus dans le Sudoku.
|
||||||
*/
|
*/
|
||||||
private List<Cell> cells = new ArrayList<>();
|
private final List<Cell> cells;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Liste des contraintes (TODO) du Sudoku.
|
* Liste des contraintes (TODO) du Sudoku.
|
||||||
*/
|
*/
|
||||||
private final List<IConstraint> constraints;
|
private final List<IConstraint> constraints;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Largeur des Blocks s'ils sont rectangulaires, valant 0 si ce n'est pas le
|
* Largeur des Blocks s'ils sont rectangulaires,
|
||||||
* cas.
|
* valant 0 si ce n'est pas le cas.
|
||||||
*/
|
*/
|
||||||
private int blockWidth;
|
private int blockWidth;
|
||||||
|
|
||||||
|
// </editor-fold>
|
||||||
|
|
||||||
|
// <editor-fold defaultstate="collapsed" desc="METHODES">
|
||||||
|
|
||||||
public Sudoku(List<Cell> cells, List<Block> blocks, List<IConstraint> constraints) {
|
public Sudoku(List<Cell> cells, List<Block> blocks, List<IConstraint> constraints) {
|
||||||
this.cells = cells;
|
this.cells = cells;
|
||||||
this.blocks = blocks;
|
this.blocks = blocks;
|
||||||
@@ -97,31 +106,79 @@ public class Sudoku {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vide la Cell dotn les coordonnées sont renseignées de son symbole.
|
* Vérifie si le Sudoku est résolue, soit complet et cohérent avec ses
|
||||||
|
* contraintes.
|
||||||
*
|
*
|
||||||
* @param x int, abscisse de la Cell voulue.
|
* @return boolean, valant true si le Sudoku est résolu, false sinon.
|
||||||
* @param y int, coordonnée de la Cell voulue.
|
|
||||||
*/
|
*/
|
||||||
|
public boolean isSolved() {
|
||||||
public void clearCell(int x, int y) {
|
boolean isComplete = isComplete();
|
||||||
assert (isValidCoords(x, y));
|
boolean isValid = isValid();
|
||||||
Cell cell = getCell(x, y);
|
return isComplete && isValid;
|
||||||
cell.setSymbolIndex(Cell.NOSYMBOL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Vide toutes les Cell du Sudoku.
|
* Vérifie que le Sudoku est complet, soit qu'il n'y ait aucune case vide.
|
||||||
|
*
|
||||||
|
* @return boolean, true si le Sudoku est complet, false sinon.
|
||||||
*/
|
*/
|
||||||
public void clear() {
|
private boolean isComplete() {
|
||||||
for (int i = 0; i < getSize() * getSize(); i++) {
|
return getFirstEmptyCell() == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vérifie si le Sudoku est valide, soit qu'il est cohérent avec ses
|
||||||
|
* contraintes.
|
||||||
|
*
|
||||||
|
* @return bollean, true si le Sudoku est valide, false sinon
|
||||||
|
*/
|
||||||
|
private boolean isValid() {
|
||||||
|
for (int i = 0; i < cells.size(); i++) {
|
||||||
Cell cell = getCell(i);
|
Cell cell = getCell(i);
|
||||||
if (cell.isMutable())
|
if (cell.isEmpty())
|
||||||
cell.setSymbolIndex(Cell.NOSYMBOL);
|
continue;
|
||||||
|
|
||||||
|
Coordinate coordinate = toCoords(i);
|
||||||
|
|
||||||
|
int symbolPlaced = cell.empty();
|
||||||
|
if (!canBePlaced(coordinate.getX(), coordinate.getY(), symbolPlaced)) {
|
||||||
|
cell.setSymbolIndex(symbolPlaced);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
cell.setSymbolIndex(symbolPlaced);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renvoie la Cell aux coordonées données.
|
||||||
|
*
|
||||||
|
* @param x int, abscisse.
|
||||||
|
* @param y int, ordonnée.
|
||||||
|
* @return Cell, si une Cell existe aux coordonnées données, null sinon.
|
||||||
|
*/
|
||||||
|
public Cell getCell(int x, int y) {
|
||||||
|
int index = toIndex(x, y);
|
||||||
|
assert (isValidCoords(x, y));
|
||||||
|
try {
|
||||||
|
return this.cells.get(index);
|
||||||
|
} catch (IndexOutOfBoundsException e) {
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getBlockWidth() {
|
/**
|
||||||
return blockWidth;
|
* Renvoie la 1re Cell vide du Sudoku.
|
||||||
|
*
|
||||||
|
* @return Cell, une Cell vide, ou null s'il n'y en a pas.
|
||||||
|
*/
|
||||||
|
public Cell getFirstEmptyCell() {
|
||||||
|
for (Cell cell : this.cells) {
|
||||||
|
if (cell.isEmpty()) {
|
||||||
|
return cell;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -189,24 +246,14 @@ public class Sudoku {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cell getCell(int x, int y) {
|
public boolean hasConstraint(Constraint constraint) {
|
||||||
int index = toIndex(x, y);
|
return this.constraints.contains(constraint.getConstraint());
|
||||||
assert (isValidCoords(x, y));
|
|
||||||
try {
|
|
||||||
return this.cells.get(index);
|
|
||||||
} catch (IndexOutOfBoundsException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cell getCell(int i) {
|
public Cell getCell(int i) {
|
||||||
return this.cells.get(i);
|
return this.cells.get(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<IConstraint> getConstraints() {
|
|
||||||
return constraints;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getSize() {
|
public int getSize() {
|
||||||
return this.blocks.size();
|
return this.blocks.size();
|
||||||
}
|
}
|
||||||
@@ -219,16 +266,19 @@ public class Sudoku {
|
|||||||
return this.blocks;
|
return this.blocks;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public List<IConstraint> getConstraints() {
|
||||||
* Vérifie si une Cell appartient au Sudoku.
|
return constraints;
|
||||||
*
|
|
||||||
* @param cell Cell, cellule dont on veut vérifier l'appartenance au Sudoku.
|
|
||||||
* @return boolean, vaut true si la Cell appartient au Sudoku.
|
|
||||||
*/
|
|
||||||
public boolean contains(Cell cell) {
|
|
||||||
return this.cells.contains(cell);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getBlockWidth() {
|
||||||
|
return blockWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setBlockWidth(int blockWidth) {
|
||||||
|
this.blockWidth = blockWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("Sudoku {");
|
sb.append("Sudoku {");
|
||||||
@@ -243,71 +293,6 @@ public class Sudoku {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
// <editor-fold defaultstate="collapsed" desc="METHODES">
|
||||||
* Renvoie la 1re Cell vide du Sudoku.
|
|
||||||
*
|
|
||||||
* @return Cell, une Cell vide, ou null s'il n'y en a pas.
|
|
||||||
*/
|
|
||||||
public Cell getFirstEmptyCell() {
|
|
||||||
for (Cell cell : this.cells) {
|
|
||||||
if (cell.isEmpty()) {
|
|
||||||
return cell;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
public boolean isSolved() {
|
|
||||||
boolean isComplete = isComplete();
|
|
||||||
boolean isValid = isValid();
|
|
||||||
return isComplete && isValid;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Vérifie que le Sudoku est complet, soit qu'il n'y ait aucune case vide.
|
|
||||||
*
|
|
||||||
* @return boolean, true si le Sudoku est complet, false sinon.
|
|
||||||
*/
|
|
||||||
private boolean isComplete() {
|
|
||||||
return getFirstEmptyCell() == null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Vérifie si le Sudoku est valide, soit qu'il est cohérent avec ses
|
|
||||||
* contraintes.
|
|
||||||
*
|
|
||||||
* @return bollean, true si le Sudoku est valide, false sinon
|
|
||||||
*/
|
|
||||||
private boolean isValid() {
|
|
||||||
for (int i = 0; i < cells.size(); i++) {
|
|
||||||
Cell cell = getCell(i);
|
|
||||||
if (cell.isEmpty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
Coordinate coordinate = toCoords(i);
|
|
||||||
|
|
||||||
int symbolPlaced = cell.empty();
|
|
||||||
if (!canBePlaced(coordinate.getX(), coordinate.getY(), symbolPlaced)) {
|
|
||||||
cell.setSymbolIndex(symbolPlaced);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
cell.setSymbolIndex(symbolPlaced);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBlockWidth(int blockWidth) {
|
|
||||||
this.blockWidth = blockWidth;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean hasConstraint(Constraint constraint) {
|
|
||||||
return this.constraints.contains(constraint.getConstraint());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,8 +27,11 @@ public class SudokuFactory {
|
|||||||
* 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 = SudokuFactory
|
public static List<IConstraint> DEFAULT_CONSTRAINTS = SudokuFactory
|
||||||
.fromConstraints(Arrays.asList(Constraint.Block, Constraint.Column,
|
.fromConstraints(Arrays.asList(
|
||||||
Constraint.Line));
|
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.
|
||||||
@@ -65,9 +68,6 @@ public class SudokuFactory {
|
|||||||
int index = ((y + blockY * height) * size + (x + blockX * width));
|
int index = ((y + blockY * height) * size + (x + blockX * width));
|
||||||
Cell blockCell = cells.get(index);
|
Cell blockCell = cells.get(index);
|
||||||
blockCell.setBlock(newBlock);
|
blockCell.setBlock(newBlock);
|
||||||
// List<Block> blockContainers = new ArrayList<>();
|
|
||||||
// blockContainers.add(newBlock);
|
|
||||||
// blockCell.setBlockContainers(blockContainers);
|
|
||||||
newBlock.addCell(blockCell);
|
newBlock.addCell(blockCell);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,7 +86,7 @@ public class SudokuFactory {
|
|||||||
*/
|
*/
|
||||||
public static MultiDoku createBasicEmptyRectangleDoku(int widthBlock, int heightBlock,
|
public static MultiDoku createBasicEmptyRectangleDoku(int widthBlock, int heightBlock,
|
||||||
List<IConstraint> constraints) {
|
List<IConstraint> constraints) {
|
||||||
return new MultiDoku(Arrays.asList(createRectangleSudoku(widthBlock, heightBlock, constraints)));
|
return new MultiDoku(List.of(createRectangleSudoku(widthBlock, heightBlock, constraints)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -96,63 +96,7 @@ public class SudokuFactory {
|
|||||||
* @return MultiDoku, MultiDoku vide.
|
* @return MultiDoku, MultiDoku vide.
|
||||||
*/
|
*/
|
||||||
public static MultiDoku createBasicEmptySquareDoku(int size, List<IConstraint> constraints) {
|
public static MultiDoku createBasicEmptySquareDoku(int size, List<IConstraint> constraints) {
|
||||||
return new MultiDoku(Arrays.asList(createSquareSudoku(size, constraints)));
|
return new MultiDoku(List.of(createSquareSudoku(size, constraints)));
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Place des Cells immutables de valeurs fournies, aux Coordinate fournies dans
|
|
||||||
* le MultiDoku doku fourni.
|
|
||||||
*
|
|
||||||
* @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) {
|
|
||||||
immutableCells.forEach((coordinate, symbol) -> {
|
|
||||||
for (Sudoku sudoku : doku.getSubGrids()) {
|
|
||||||
Cell cell = sudoku.getCell(coordinate.getX(), coordinate.getY());
|
|
||||||
if (cell != null) {
|
|
||||||
cell.setSymbolIndex(symbol);
|
|
||||||
cell.setImmutable();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Créée un MultiDoku de difficulté difficulty à partir d'un MultiDoku fourni.
|
|
||||||
*
|
|
||||||
* @param doku MultiDoku, MultiDoku dont on doit vider des Cells.
|
|
||||||
* @param nbCellsToEmpty int, nombre de cases à retirer.
|
|
||||||
* @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, Solver solver) throws Exception {
|
|
||||||
|
|
||||||
if (nbCellsToEmpty >= doku.getCells().size()) {
|
|
||||||
throw new Exception();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (nbCellsToEmpty == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Cell> cellsThatCanBeEmptied = doku.getFilledCells();
|
|
||||||
|
|
||||||
int index = random.nextInt(cellsThatCanBeEmptied.size());
|
|
||||||
Cell cellToEmpty = cellsThatCanBeEmptied.get(index);
|
|
||||||
|
|
||||||
cellToEmpty.empty();
|
|
||||||
|
|
||||||
int nbDokuSultions = solver.countSolution(doku);
|
|
||||||
if (nbDokuSultions == 1) {
|
|
||||||
return newDokuFromFilledOne(doku, --nbCellsToEmpty, solver);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -227,6 +171,9 @@ public class SudokuFactory {
|
|||||||
* Créée un MultiDoku de Blocks carrés de taille size composé de cinq Sudokus,
|
* 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
|
* dont un central qui partage chacun de ses Blockss d'angle avec un autre
|
||||||
* Sudoku.
|
* Sudoku.
|
||||||
|
* 2 3
|
||||||
|
* 1
|
||||||
|
* 4 5
|
||||||
*
|
*
|
||||||
* @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.
|
||||||
@@ -234,45 +181,25 @@ public class SudokuFactory {
|
|||||||
public static MultiDoku createBasicXShapedMultidoku(int size, List<IConstraint> constraints) {
|
public static MultiDoku createBasicXShapedMultidoku(int size, List<IConstraint> constraints) {
|
||||||
assert (size > 1);
|
assert (size > 1);
|
||||||
|
|
||||||
/*
|
return createBasicXShapedMultidoku(size, size, constraints);
|
||||||
* 2 3
|
|
||||||
* 1
|
|
||||||
* 4 5
|
|
||||||
*/
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
linkRectangleSudokus(sudoku1, sudoku2, new Coordinate(1 - size, 1 - size));
|
|
||||||
linkRectangleSudokus(sudoku1, sudoku3, new Coordinate(size - 1, 1 - size));
|
|
||||||
linkRectangleSudokus(sudoku1, sudoku4, new Coordinate(1 - size, size - 1));
|
|
||||||
linkRectangleSudokus(sudoku1, sudoku5, new Coordinate(size - 1, size - 1));
|
|
||||||
|
|
||||||
return new MultiDoku(Arrays.asList(sudoku1, sudoku2, sudoku3, sudoku4, sudoku5));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Créée un MultiDoku de Blocks rectangulaires de forme width par height composé
|
* Créée un MultiDoku de Blocks rectangulaires de forme X, de taille width par height composé
|
||||||
* de cinq Sudokus,
|
* de cinq Sudokus,
|
||||||
* dont un central qui partage chacun de ses Blocks d'angle avec un autre
|
* dont un central qui partage chacun de ses Blocks d'angle avec un autre
|
||||||
* Sudoku.
|
* Sudoku.
|
||||||
|
* 2 3
|
||||||
|
* 1
|
||||||
|
* 4 5
|
||||||
*
|
*
|
||||||
* @param width int, largeur des Blocks unitraires des Sudokus à crééer.
|
* @param width int, largeur des Blocks unitaires des Sudokus à crééer.
|
||||||
* @param height int, hauteur des Blocks unitraires des Sudokus à crééer.
|
* @param height int, hauteur des Blocks unitaires des Sudokus à crééer.
|
||||||
* @return MultiDoku, MultiDoku de forme X.
|
* @return MultiDoku, MultiDoku de forme X.
|
||||||
*/
|
*/
|
||||||
public static MultiDoku createBasicXShapedMultidoku(int width, int height, List<IConstraint> constraints) {
|
public static MultiDoku createBasicXShapedMultidoku(int width, int height, List<IConstraint> constraints) {
|
||||||
assert (width > 1 && height > 1);
|
assert (width > 1 && height > 1);
|
||||||
|
|
||||||
/*
|
|
||||||
* 2 3
|
|
||||||
* 1
|
|
||||||
* 4 5
|
|
||||||
*/
|
|
||||||
|
|
||||||
Sudoku sudoku1 = createRectangleSudoku(width, height, constraints);
|
Sudoku sudoku1 = createRectangleSudoku(width, height, constraints);
|
||||||
Sudoku sudoku2 = createRectangleSudoku(width, height, constraints);
|
Sudoku sudoku2 = createRectangleSudoku(width, height, constraints);
|
||||||
Sudoku sudoku3 = createRectangleSudoku(width, height, constraints);
|
Sudoku sudoku3 = createRectangleSudoku(width, height, constraints);
|
||||||
@@ -287,30 +214,65 @@ public class SudokuFactory {
|
|||||||
return new MultiDoku(Arrays.asList(sudoku1, sudoku2, sudoku3, sudoku4, sudoku5));
|
return new MultiDoku(Arrays.asList(sudoku1, sudoku2, sudoku3, sudoku4, sudoku5));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void fillDoku(MultiDoku doku, Difficulty difficulty) throws Exception {
|
/**
|
||||||
Solver solver = new RandomSolver();
|
* Créée un MultiDoku de Blocks carrés de forme +, de taille size composé
|
||||||
solver.solve(doku);
|
* de cinq Sudokus,
|
||||||
int nbCellsToEmpty = (int) (difficulty.getFactor() * doku.getNbCells());
|
* dont un central qui partage chacun de ses Blocks de bord avec un autre
|
||||||
boolean successfull = newDokuFromFilledOne(doku, nbCellsToEmpty, solver);
|
* Sudoku.
|
||||||
doku.setFilledCellsImmutable();
|
* 3
|
||||||
|
* 2 1 4
|
||||||
|
* 5
|
||||||
|
*
|
||||||
|
* @param size int, largeur des Blocks unitaires des Sudokus à crééer.
|
||||||
|
* @return MultiDoku, MultiDoku de forme +.
|
||||||
|
*/
|
||||||
|
public static MultiDoku createBasicPlusShapedMultidoku(int size, List<IConstraint> constraints) {
|
||||||
|
assert (size > 1);
|
||||||
|
|
||||||
|
return createBasicPlusShapedMultidoku(size, size, constraints);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static MultiDoku fromfile(String filePath) {
|
/**
|
||||||
try {
|
* Créée un MultiDoku de Blocks rectangulaires de forme +, de taille width par height composé
|
||||||
String content = Files.readString(Paths.get(filePath));
|
* de cinq Sudokus,
|
||||||
MultiDoku doku = SudokuSerializer.deserializeSudoku(content);
|
* dont un central qui partage chacun de ses Blocks de bord avec un autre
|
||||||
return doku;
|
* Sudoku.
|
||||||
} catch (IOException e) {
|
* 3
|
||||||
e.printStackTrace();
|
* 2 1 4
|
||||||
return null;
|
* 5
|
||||||
}
|
*
|
||||||
|
* @param width int, largeur des Blocks unitaires des Sudokus à crééer.
|
||||||
|
* @param height int, hauteur des Blocks unitaires des Sudokus à crééer.
|
||||||
|
* @return MultiDoku, MultiDoku de forme +.
|
||||||
|
*/
|
||||||
|
public static MultiDoku createBasicPlusShapedMultidoku(int width, int height, List<IConstraint> constraints) {
|
||||||
|
assert (width > 1 && height > 1);
|
||||||
|
|
||||||
|
Sudoku sudoku1 = createRectangleSudoku(width, height, constraints);
|
||||||
|
Sudoku sudoku2 = createRectangleSudoku(width, height, constraints);
|
||||||
|
Sudoku sudoku3 = createRectangleSudoku(width, height, constraints);
|
||||||
|
Sudoku sudoku4 = createRectangleSudoku(width, height, constraints);
|
||||||
|
Sudoku sudoku5 = createRectangleSudoku(width, height, constraints);
|
||||||
|
|
||||||
|
linkRectangleSudokus(sudoku1, sudoku2, new Coordinate(1 - height, 0));
|
||||||
|
linkRectangleSudokus(sudoku1, sudoku3, new Coordinate(0, 1 - width));
|
||||||
|
linkRectangleSudokus(sudoku1, sudoku4, new Coordinate(height - 1, 0));
|
||||||
|
linkRectangleSudokus(sudoku1, sudoku5, new Coordinate(0, width - 1));
|
||||||
|
|
||||||
|
return new MultiDoku(Arrays.asList(sudoku1, sudoku2, sudoku3, sudoku4, sudoku5));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crée un Sudoku vide avec les Blocks aléatoires.
|
||||||
|
*
|
||||||
|
* @param blockSize int, la taille des Block.
|
||||||
|
* @param constraints List<IConstraint>, la liste des contraintes.
|
||||||
|
* @return MultiDoku, avec un seul Sudoku.
|
||||||
|
*/
|
||||||
public static MultiDoku createBasicEmptyRandomBlockDoku(int blockSize, List<IConstraint> constraints) {
|
public static MultiDoku createBasicEmptyRandomBlockDoku(int blockSize, List<IConstraint> constraints) {
|
||||||
int blockCellCount = blockSize * blockSize;
|
int blockCellCount = blockSize * blockSize;
|
||||||
List<Cell> cells = initCells(blockCellCount);
|
List<Cell> cells = initCells(blockCellCount);
|
||||||
List<Cell> homeLessCells = new ArrayList<>();
|
List<Cell> homeLessCells = new ArrayList<>(cells);
|
||||||
homeLessCells.addAll(cells);
|
|
||||||
List<Block> blocks = new ArrayList<>();
|
List<Block> blocks = new ArrayList<>();
|
||||||
Random r = new Random();
|
Random r = new Random();
|
||||||
for (int i = 0; i < blockCellCount; i++) {
|
for (int i = 0; i < blockCellCount; i++) {
|
||||||
@@ -327,9 +289,79 @@ public class SudokuFactory {
|
|||||||
for (Block block : blocks) {
|
for (Block block : blocks) {
|
||||||
block.getSudokus().add(sudoku);
|
block.getSudokus().add(sudoku);
|
||||||
}
|
}
|
||||||
return new MultiDoku(Arrays.asList(sudoku));
|
return new MultiDoku(List.of(sudoku));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Créée un MultiDoku de difficulté difficulty à partir d'un MultiDoku fourni.
|
||||||
|
*
|
||||||
|
* @param doku MultiDoku, MultiDoku dont on doit vider des Cells.
|
||||||
|
* @param nbCellsToEmpty int, nombre de cases à retirer.
|
||||||
|
* @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, Solver solver) throws Exception {
|
||||||
|
|
||||||
|
if (nbCellsToEmpty >= doku.getCells().size()) {
|
||||||
|
throw new Exception();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nbCellsToEmpty == 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Cell> cellsThatCanBeEmptied = doku.getFilledCells();
|
||||||
|
|
||||||
|
int index = random.nextInt(cellsThatCanBeEmptied.size());
|
||||||
|
Cell cellToEmpty = cellsThatCanBeEmptied.get(index);
|
||||||
|
|
||||||
|
cellToEmpty.empty();
|
||||||
|
|
||||||
|
int nbDokuSultions = solver.countSolution(doku);
|
||||||
|
if (nbDokuSultions == 1) {
|
||||||
|
return newDokuFromFilledOne(doku, --nbCellsToEmpty, solver);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remplit un MultiDoku donné par rapport à une difficulté.
|
||||||
|
*
|
||||||
|
* @param doku MultiDoku, vide.
|
||||||
|
* @param difficulty Difficulty, qui correspond au pourcentage de cases à enlever.
|
||||||
|
*/
|
||||||
|
public static void fillDoku(MultiDoku doku, Difficulty difficulty) throws Exception {
|
||||||
|
Solver solver = new RandomSolver();
|
||||||
|
solver.solve(doku);
|
||||||
|
int nbCellsToEmpty = (int) (difficulty.getFactor() * doku.getNbCells());
|
||||||
|
newDokuFromFilledOne(doku, nbCellsToEmpty, solver);
|
||||||
|
doku.setFilledCellsImmutable();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Génère un MultiDoku à partir d'un fichier de sauvegarde.
|
||||||
|
*
|
||||||
|
* @param filePath String, chemin du fichier.
|
||||||
|
* @return MultiDoku.
|
||||||
|
*/
|
||||||
|
public static MultiDoku fromfile(String filePath) {
|
||||||
|
try {
|
||||||
|
String content = Files.readString(Paths.get(filePath));
|
||||||
|
return SudokuSerializer.deserializeSudoku(content);
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transforme des Constraint en IConstraint correspondants
|
||||||
|
* @param constraints List<Constraints>
|
||||||
|
* @return List<IConstraints>
|
||||||
|
*/
|
||||||
public static List<IConstraint> fromConstraints(List<Constraint> constraints) {
|
public static List<IConstraint> fromConstraints(List<Constraint> constraints) {
|
||||||
List<IConstraint> iconstraints = new ArrayList<>();
|
List<IConstraint> iconstraints = new ArrayList<>();
|
||||||
for (Constraint cons : constraints) {
|
for (Constraint cons : constraints) {
|
||||||
|
|||||||
@@ -58,18 +58,20 @@ public class SudokuSerializerTest {
|
|||||||
void testSerialize() {
|
void testSerialize() {
|
||||||
Random r = new Random();
|
Random r = new Random();
|
||||||
int testCount = 20;
|
int testCount = 20;
|
||||||
|
int minSize = 2;
|
||||||
|
int maxSize = 3;
|
||||||
for (int i = 0; i < testCount; i++) {
|
for (int i = 0; i < testCount; i++) {
|
||||||
int blockWidth = r.nextInt(10) + 1;
|
int blockWidth = r.nextInt(maxSize - minSize + 1) + minSize;
|
||||||
int blockHeight = r.nextInt(10) + 1;
|
int blockHeight = r.nextInt(maxSize - minSize + 1) + minSize;
|
||||||
testSerializeWithSize(blockWidth, blockHeight);
|
testSerializeWithSize(blockWidth, blockHeight);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < testCount; i++) {
|
for (int i = 0; i < testCount; i++) {
|
||||||
int blockWidth = r.nextInt(10) + 1;
|
int blockWidth = r.nextInt(maxSize - minSize + 1) + minSize;
|
||||||
int blockHeight = r.nextInt(10) + 1;
|
int blockHeight = r.nextInt(maxSize - minSize + 1) + minSize;
|
||||||
testSaveWithSize(blockWidth, blockHeight);
|
testSaveWithSize(blockWidth, blockHeight);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < testCount; i++) {
|
for (int i = 0; i < testCount; i++) {
|
||||||
int size = r.nextInt(10) + 1;
|
int size = r.nextInt(maxSize - minSize + 1) + minSize;
|
||||||
testSerializeX(size);
|
testSerializeX(size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,16 +1,21 @@
|
|||||||
package sudoku.solver;
|
package sudoku.solver;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.junit.jupiter.api.BeforeAll;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import gui.constants.Symbols;
|
|
||||||
import sudoku.io.SudokuPrinter;
|
|
||||||
import sudoku.io.SudokuSerializer;
|
import sudoku.io.SudokuSerializer;
|
||||||
import sudoku.structure.*;
|
import sudoku.structure.Cell;
|
||||||
|
import sudoku.structure.Difficulty;
|
||||||
|
import sudoku.structure.MultiDoku;
|
||||||
|
import sudoku.structure.Sudoku;
|
||||||
|
import sudoku.structure.SudokuFactory;
|
||||||
|
|
||||||
class SolverTest {
|
class SolverTest {
|
||||||
private int ns = Cell.NOSYMBOL;
|
private int ns = Cell.NOSYMBOL;
|
||||||
@@ -19,13 +24,13 @@ class SolverTest {
|
|||||||
private static MixedSolver m;
|
private static MixedSolver m;
|
||||||
|
|
||||||
@BeforeAll
|
@BeforeAll
|
||||||
public static void initializeSolvers(){
|
public static void initializeSolvers() {
|
||||||
h = new HumanSolver();
|
h = new HumanSolver();
|
||||||
r = new RandomSolver();
|
r = new RandomSolver();
|
||||||
m = new MixedSolver();
|
m = new MixedSolver();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testSize2(Solver solver){
|
private void testSize2(Solver solver) {
|
||||||
MultiDoku mdTest = SudokuFactory.createBasicEmptySquareDoku(2, SudokuFactory.DEFAULT_CONSTRAINTS);
|
MultiDoku mdTest = SudokuFactory.createBasicEmptySquareDoku(2, SudokuFactory.DEFAULT_CONSTRAINTS);
|
||||||
MultiDoku mdResult = SudokuFactory.createBasicEmptySquareDoku(2, SudokuFactory.DEFAULT_CONSTRAINTS);
|
MultiDoku mdResult = SudokuFactory.createBasicEmptySquareDoku(2, SudokuFactory.DEFAULT_CONSTRAINTS);
|
||||||
Sudoku test = mdTest.getSubGrid(0);
|
Sudoku test = mdTest.getSubGrid(0);
|
||||||
@@ -35,25 +40,23 @@ class SolverTest {
|
|||||||
0, ns, ns, ns,
|
0, ns, ns, ns,
|
||||||
ns, ns, ns, 3,
|
ns, ns, ns, 3,
|
||||||
ns, 0, 1, ns);
|
ns, 0, 1, ns);
|
||||||
assert (test.setImmutableCellsSymbol(immutableCells));
|
assertTrue(test.setImmutableCellsSymbol(immutableCells));
|
||||||
List<Integer> correctCells = List.of(
|
List<Integer> correctCells = List.of(
|
||||||
1, 2, 3, 0,
|
1, 2, 3, 0,
|
||||||
0, 3, 2, 1,
|
0, 3, 2, 1,
|
||||||
2, 1, 0, 3,
|
2, 1, 0, 3,
|
||||||
3, 0, 1, 2);
|
3, 0, 1, 2);
|
||||||
assert(result.setCellsSymbol(correctCells));
|
assertTrue(result.setCellsSymbol(correctCells));
|
||||||
assert(result.isSolved());
|
assertTrue(result.isSolved());
|
||||||
|
|
||||||
|
assertNotEquals(mdResult, mdTest);
|
||||||
solver.solve(mdTest);
|
solver.solve(mdTest);
|
||||||
assert (mdTest.isSolved());
|
assertTrue(mdTest.isSolved());
|
||||||
for (Cell cell : test.getCells()) {
|
|
||||||
cell.setImmutable();
|
assertEquals(mdTest, mdResult);
|
||||||
}
|
|
||||||
assertEquals(SudokuSerializer.serializeSudoku(mdTest).toString(),
|
|
||||||
SudokuSerializer.serializeSudoku(mdResult).toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testSize3(Solver solver){
|
private void testSize3(Solver solver) {
|
||||||
MultiDoku mdTest = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
|
MultiDoku mdTest = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
|
||||||
MultiDoku mdResult = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
|
MultiDoku mdResult = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
|
||||||
Sudoku test = mdTest.getSubGrid(0);
|
Sudoku test = mdTest.getSubGrid(0);
|
||||||
@@ -79,46 +82,51 @@ class SolverTest {
|
|||||||
3, 1, 6, 0, 2, 8, 4, 7, 5,
|
3, 1, 6, 0, 2, 8, 4, 7, 5,
|
||||||
8, 0, 4, 7, 6, 5, 2, 1, 3,
|
8, 0, 4, 7, 6, 5, 2, 1, 3,
|
||||||
5, 7, 2, 4, 3, 1, 0, 6, 8);
|
5, 7, 2, 4, 3, 1, 0, 6, 8);
|
||||||
assert(result.setCellsSymbol(correctCells));
|
assert (result.setCellsSymbol(correctCells));
|
||||||
assert(result.isSolved());
|
assert (result.isSolved());
|
||||||
|
|
||||||
|
assertNotEquals(mdResult, mdTest);
|
||||||
solver.solve(mdTest);
|
solver.solve(mdTest);
|
||||||
assert (mdTest.isSolved());
|
assert (mdTest.isSolved());
|
||||||
for (Cell cell : test.getCells()) {
|
assertEquals(mdTest, mdResult);
|
||||||
cell.setImmutable();
|
|
||||||
}
|
|
||||||
assertEquals(SudokuSerializer.serializeSudoku(mdTest).toString(),
|
|
||||||
SudokuSerializer.serializeSudoku(mdResult).toString());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void testMDSize3(Solver solver){
|
private void testMDSize3(Solver solver) {
|
||||||
MultiDoku mdTest = SudokuFactory.createBasicXShapedMultidoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
|
MultiDoku mdTest = SudokuFactory.createBasicXShapedMultidoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
|
||||||
try {
|
try {
|
||||||
SudokuFactory.fillDoku(mdTest, Difficulty.Easy);
|
SudokuFactory.fillDoku(mdTest, Difficulty.Easy);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
assert(false);
|
assert (false);
|
||||||
}
|
}
|
||||||
MultiDoku result = SudokuSerializer.deserializeSudoku(SudokuSerializer.serializeSudoku(mdTest));
|
MultiDoku mdResult = SudokuSerializer.deserializeSudoku(SudokuSerializer.serializeSudoku(mdTest));
|
||||||
assert(result.isSolved());
|
assertFalse(mdTest.isSolved());
|
||||||
solver.solve(mdTest);
|
assertFalse(mdResult.isSolved());
|
||||||
assert (mdTest.isSolved());
|
assertEquals(mdTest, mdResult);
|
||||||
for (Cell cell : mdTest.getCells()) {
|
|
||||||
cell.setImmutable();
|
assertTrue(solver.solve(mdTest));
|
||||||
}
|
|
||||||
assertEquals(SudokuSerializer.serializeSudoku(mdTest).toString(),
|
assertTrue(mdTest.isSolved());
|
||||||
SudokuSerializer.serializeSudoku(result).toString());
|
assertFalse(mdResult.isSolved());
|
||||||
|
System.out.println(mdTest);
|
||||||
|
System.out.println(mdResult);
|
||||||
|
assertNotEquals(mdTest, mdResult);
|
||||||
|
|
||||||
|
assertTrue(solver.solve(mdResult));
|
||||||
|
|
||||||
|
assertEquals(mdTest, mdResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void solveTest() {
|
void solveTest() {
|
||||||
testSize2(h);
|
for (int i = 0; i < 100; i++) {
|
||||||
testSize3(h);
|
testSize2(h);
|
||||||
testMDSize3(h);
|
testSize3(h);
|
||||||
testSize2(m);
|
testSize2(m);
|
||||||
testSize3(m);
|
testSize3(m);
|
||||||
testMDSize3(m);
|
testMDSize3(m);
|
||||||
testSize2(r);
|
testSize2(r);
|
||||||
testSize3(r);
|
testSize3(r);
|
||||||
testMDSize3(r);
|
testMDSize3(r);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user