Merge remote-tracking branch 'origin/master'
# Conflicts: # app/src/main/java/sudoku/structure/MultiDoku.java # app/src/main/java/sudoku/structure/Sudoku.java # app/src/main/java/sudoku/structure/SudokuFactory.java # app/src/test/java/sudoku/solver/SolverTest.java
This commit is contained in:
21
README.md
Normal file
21
README.md
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Sudoku 🧩
|
||||||
|
|
||||||
|
## Features 🌟
|
||||||
|
|
||||||
|
- Graphical User Interface (GUI)
|
||||||
|
- Sudoku saves
|
||||||
|
- Multiplayer
|
||||||
|
|
||||||
|
## Develop ☝🤓
|
||||||
|
|
||||||
|
### Run 🏃
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./gradlew run
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run tests 🗣️🔥
|
||||||
|
|
||||||
|
```sh
|
||||||
|
./gradlew test
|
||||||
|
```
|
||||||
@@ -27,6 +27,10 @@ public class RenderableMultidoku {
|
|||||||
this.doku = doku;
|
this.doku = doku;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isResolved() {
|
||||||
|
return this.doku.isValid();
|
||||||
|
}
|
||||||
|
|
||||||
public int getWidth() {
|
public int getWidth() {
|
||||||
return width;
|
return width;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,23 @@
|
|||||||
package gui;
|
package gui;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import common.Signal;
|
||||||
import gui.ColorGenerator.Color;
|
import gui.ColorGenerator.Color;
|
||||||
import imgui.ImGui;
|
import imgui.ImGui;
|
||||||
import imgui.ImVec2;
|
import imgui.ImVec2;
|
||||||
import imgui.ImVec4;
|
import imgui.ImVec4;
|
||||||
import imgui.flag.ImGuiCol;
|
import imgui.flag.ImGuiCol;
|
||||||
import imgui.flag.ImGuiStyleVar;
|
import imgui.flag.ImGuiStyleVar;
|
||||||
|
import sudoku.constraint.Constraint;
|
||||||
import sudoku.structure.Block;
|
import sudoku.structure.Block;
|
||||||
import sudoku.structure.Cell;
|
import sudoku.structure.Cell;
|
||||||
import sudoku.structure.MultiDoku;
|
import sudoku.structure.MultiDoku;
|
||||||
|
import sudoku.structure.Sudoku;
|
||||||
|
|
||||||
public class SudokuRenderer {
|
public class SudokuRenderer {
|
||||||
|
|
||||||
@@ -22,11 +27,28 @@ public class SudokuRenderer {
|
|||||||
|
|
||||||
private static final ImVec4 BLACK = new ImVec4(0, 0, 0, 1);
|
private static final ImVec4 BLACK = new ImVec4(0, 0, 0, 1);
|
||||||
private static final ImVec4 TRANSPARENT = new ImVec4();
|
private static final ImVec4 TRANSPARENT = new ImVec4();
|
||||||
|
private static final ImVec4 WHITE = new ImVec4(1.0f, 1.0f, 1.0f, 1.0f);
|
||||||
private static final ImVec2 cellSize = new ImVec2(50, 50);
|
private static final ImVec2 cellSize = new ImVec2(50, 50);
|
||||||
|
|
||||||
|
private final Set<Cell> diagonals = new HashSet<>();
|
||||||
|
|
||||||
|
public final Signal onResolve = new Signal();
|
||||||
|
|
||||||
public SudokuRenderer(MultiDoku doku) {
|
public SudokuRenderer(MultiDoku doku) {
|
||||||
this.doku = RenderableMultidoku.fromMultidoku(doku);
|
this.doku = RenderableMultidoku.fromMultidoku(doku);
|
||||||
this.colorPalette = initColors();
|
this.colorPalette = initColors();
|
||||||
|
initDiagonals();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initDiagonals() {
|
||||||
|
for (Sudoku sudoku : this.doku.getDoku().getSubGrids()) {
|
||||||
|
if (sudoku.hasConstraint(Constraint.Diagonal)) {
|
||||||
|
for (int i = 0; i < sudoku.getSize(); i++) {
|
||||||
|
this.diagonals.add(sudoku.getCell(i, i));
|
||||||
|
this.diagonals.add(sudoku.getCell(sudoku.getSize() - i - 1, i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<Block, Color> initColors() {
|
private Map<Block, Color> initColors() {
|
||||||
@@ -55,6 +77,8 @@ public class SudokuRenderer {
|
|||||||
} else {
|
} else {
|
||||||
if (ImGui.button(Integer.toString(i + 1), cellSize)) {
|
if (ImGui.button(Integer.toString(i + 1), cellSize)) {
|
||||||
this.doku.setCellValue(currentCell, i);
|
this.doku.setCellValue(currentCell, i);
|
||||||
|
if (this.doku.isResolved())
|
||||||
|
this.onResolve.emit();
|
||||||
ImGui.closeCurrentPopup();
|
ImGui.closeCurrentPopup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -66,7 +90,11 @@ public class SudokuRenderer {
|
|||||||
public void render() {
|
public void render() {
|
||||||
final float sudokuViewWidth = cellSize.x * doku.getWidth();
|
final float sudokuViewWidth = cellSize.x * doku.getWidth();
|
||||||
final float displayWidth = ImGui.getIO().getDisplaySizeX();
|
final float displayWidth = ImGui.getIO().getDisplaySizeX();
|
||||||
ImGui.setCursorPosX(displayWidth / 2.0f - sudokuViewWidth / 2.0f);
|
float offsetX = displayWidth / 2.0f - sudokuViewWidth / 2.0f;
|
||||||
|
// if the grid is too big, don't offset it
|
||||||
|
if (offsetX > 0) {
|
||||||
|
ImGui.setCursorPosX(offsetX);
|
||||||
|
}
|
||||||
ImGui.beginChild(1, new ImVec2(cellSize.x * doku.getWidth(), cellSize.y * doku.getHeight()));
|
ImGui.beginChild(1, new ImVec2(cellSize.x * doku.getWidth(), cellSize.y * doku.getHeight()));
|
||||||
|
|
||||||
ImGui.pushStyleVar(ImGuiStyleVar.FrameBorderSize, 2.0f);
|
ImGui.pushStyleVar(ImGuiStyleVar.FrameBorderSize, 2.0f);
|
||||||
@@ -81,16 +109,16 @@ public class SudokuRenderer {
|
|||||||
ImGui.pushStyleColor(ImGuiCol.Border, TRANSPARENT);
|
ImGui.pushStyleColor(ImGuiCol.Border, TRANSPARENT);
|
||||||
ImGui.pushStyleColor(ImGuiCol.Button, TRANSPARENT);
|
ImGui.pushStyleColor(ImGuiCol.Button, TRANSPARENT);
|
||||||
ImGui.button("##" + index, cellSize);
|
ImGui.button("##" + index, cellSize);
|
||||||
|
} else {
|
||||||
|
if (diagonals.contains(cell)) {
|
||||||
|
ImGui.pushStyleColor(ImGuiCol.Border, WHITE);
|
||||||
} else {
|
} else {
|
||||||
ImGui.pushStyleColor(ImGuiCol.Border, BLACK);
|
ImGui.pushStyleColor(ImGuiCol.Border, BLACK);
|
||||||
|
}
|
||||||
int symbol = cell.getSymbolIndex();
|
int symbol = cell.getSymbolIndex();
|
||||||
Color blockColor = colorPalette.get(cell.getBlock());
|
Color blockColor = colorPalette.get(cell.getBlock());
|
||||||
if (!cell.isMutable()) {
|
if (!cell.isMutable()) {
|
||||||
// ImGui.pushFont(Fonts.ARIAL_BOLD);
|
|
||||||
blockColor = new Color(blockColor.r - 0.20f, blockColor.g - 0.20f, blockColor.b - 0.20f);
|
blockColor = new Color(blockColor.r - 0.20f, blockColor.g - 0.20f, blockColor.b - 0.20f);
|
||||||
// ImGui.pushStyleColor(ImGuiCol.Text, new ImVec4(0.1f, 0.1f, 0.1f, 1.0f));
|
|
||||||
} else {
|
|
||||||
// ImGui.pushFont(Fonts.CHERI);
|
|
||||||
}
|
}
|
||||||
ImGui.pushStyleColor(ImGuiCol.Button, new ImVec4(blockColor.r, blockColor.g, blockColor.b, 1.0f));
|
ImGui.pushStyleColor(ImGuiCol.Button, new ImVec4(blockColor.r, blockColor.g, blockColor.b, 1.0f));
|
||||||
String cellText = "";
|
String cellText = "";
|
||||||
@@ -100,10 +128,6 @@ public class SudokuRenderer {
|
|||||||
ImGui.openPopup("editPopup");
|
ImGui.openPopup("editPopup");
|
||||||
currentCell = cell;
|
currentCell = cell;
|
||||||
}
|
}
|
||||||
if (!cell.isMutable()) {
|
|
||||||
// ImGui.popStyleColor();
|
|
||||||
}
|
|
||||||
// ImGui.popFont();
|
|
||||||
}
|
}
|
||||||
ImGui.popStyleColor(2);
|
ImGui.popStyleColor(2);
|
||||||
}
|
}
|
||||||
|
|||||||
147
app/src/main/java/gui/SudokuSelector.java
Normal file
147
app/src/main/java/gui/SudokuSelector.java
Normal file
@@ -0,0 +1,147 @@
|
|||||||
|
package gui;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import common.Signal;
|
||||||
|
import imgui.ImGui;
|
||||||
|
import imgui.extension.imguifiledialog.ImGuiFileDialog;
|
||||||
|
import imgui.extension.imguifiledialog.flag.ImGuiFileDialogFlags;
|
||||||
|
import imgui.type.ImBoolean;
|
||||||
|
import imgui.type.ImInt;
|
||||||
|
import sudoku.constraint.Constraint;
|
||||||
|
import sudoku.structure.Difficulty;
|
||||||
|
import sudoku.structure.MultiDoku;
|
||||||
|
import sudoku.structure.SudokuFactory;
|
||||||
|
|
||||||
|
public class SudokuSelector {
|
||||||
|
|
||||||
|
public final Signal onSelect = new Signal();
|
||||||
|
private MultiDoku doku;
|
||||||
|
|
||||||
|
private final boolean canGenEmptyGrid;
|
||||||
|
|
||||||
|
private final ImInt sudokuType = new ImInt(0);
|
||||||
|
|
||||||
|
private final ImInt difficulty = new ImInt(Difficulty.Medium.ordinal());
|
||||||
|
private final List<ImBoolean> contraints = new ArrayList<>();
|
||||||
|
|
||||||
|
private static final String[] sudokuTypes = { "Carré", "Rectangle", "Multidoku" };
|
||||||
|
private static final int SQUARE = 0, RECTANGLE = 1, MULTIDOKU = 2;
|
||||||
|
|
||||||
|
private final ImInt sudokuSize = new ImInt(3);
|
||||||
|
|
||||||
|
private final ImInt sudokuWidth = new ImInt(3);
|
||||||
|
private final ImInt sudokuHeight = new ImInt(3);
|
||||||
|
|
||||||
|
public SudokuSelector(boolean canGenEmptyGrid) {
|
||||||
|
this.canGenEmptyGrid = canGenEmptyGrid;
|
||||||
|
initConstraints();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Constraint> getConstraints() {
|
||||||
|
List<Constraint> constraints = new ArrayList<>();
|
||||||
|
for (int i = 0; i < this.contraints.size(); i++) {
|
||||||
|
if (this.contraints.get(i).get())
|
||||||
|
constraints.add(Constraint.values()[i]);
|
||||||
|
}
|
||||||
|
return constraints;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initConstraints() {
|
||||||
|
for (Constraint cons : Constraint.values()) {
|
||||||
|
contraints.add(new ImBoolean(SudokuFactory.DEFAULT_CONSTRAINTS.contains(cons)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void selectSudoku(MultiDoku doku, boolean empty) {
|
||||||
|
this.doku = doku;
|
||||||
|
if (!empty) {
|
||||||
|
try {
|
||||||
|
SudokuFactory.fillDoku(doku, Difficulty.values()[difficulty.get()]);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.onSelect.emit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void renderFileDialog() {
|
||||||
|
if (ImGuiFileDialog.display("browse-sudoku", ImGuiFileDialogFlags.None)) {
|
||||||
|
if (ImGuiFileDialog.isOk()) {
|
||||||
|
var selection = ImGuiFileDialog.getSelection();
|
||||||
|
for (var entry : selection.entrySet()) {
|
||||||
|
try {
|
||||||
|
String filePath = entry.getValue();
|
||||||
|
this.doku = SudokuFactory.fromfile(filePath);
|
||||||
|
if (this.doku != null)
|
||||||
|
this.onSelect.emit();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGuiFileDialog.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void render() {
|
||||||
|
ImGui.combo("Type de Sudoku", sudokuType, sudokuTypes);
|
||||||
|
ImGui.combo("Difficulté", difficulty, Difficulty.getDifficultyNames());
|
||||||
|
if (ImGui.treeNode("Constraintes")) {
|
||||||
|
for (Constraint cons : Constraint.values()) {
|
||||||
|
ImGui.checkbox(cons.getDisplayName(), contraints.get(cons.ordinal()));
|
||||||
|
}
|
||||||
|
ImGui.treePop();
|
||||||
|
}
|
||||||
|
switch (sudokuType.get()) {
|
||||||
|
case SQUARE:
|
||||||
|
ImGui.inputInt("Taille", sudokuSize);
|
||||||
|
if (ImGui.button("Résoudre un sudoku")) {
|
||||||
|
selectSudoku(SudokuFactory.createBasicEmptySquareDoku(sudokuSize.get(), getConstraints()), false);
|
||||||
|
}
|
||||||
|
if (canGenEmptyGrid && ImGui.button("Générer une grille vide")) {
|
||||||
|
selectSudoku(SudokuFactory.createBasicEmptySquareDoku(sudokuSize.get(), getConstraints()), true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case RECTANGLE:
|
||||||
|
ImGui.inputInt("Largeur", sudokuHeight);
|
||||||
|
ImGui.inputInt("Longueur", sudokuWidth);
|
||||||
|
if (ImGui.button("Résoudre un sudoku")) {
|
||||||
|
selectSudoku(
|
||||||
|
SudokuFactory.createBasicEmptyRectangleDoku(sudokuWidth.get(), sudokuHeight.get(),
|
||||||
|
getConstraints()),
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
if (canGenEmptyGrid && ImGui.button("Générer une grille vide")) {
|
||||||
|
selectSudoku(
|
||||||
|
SudokuFactory.createBasicEmptyRectangleDoku(sudokuWidth.get(), sudokuHeight.get(),
|
||||||
|
getConstraints()),
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case MULTIDOKU:
|
||||||
|
ImGui.inputInt("Taille", sudokuSize);
|
||||||
|
if (ImGui.button("Résoudre un sudoku")) {
|
||||||
|
selectSudoku(SudokuFactory.createBasicXShapedMultidoku(sudokuSize.get(), getConstraints()), false);
|
||||||
|
}
|
||||||
|
if (canGenEmptyGrid && ImGui.button("Générer une grille vide")) {
|
||||||
|
selectSudoku(SudokuFactory.createBasicXShapedMultidoku(sudokuSize.get(), getConstraints()), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ImGui.button("À partir d'un fichier")) {
|
||||||
|
ImGuiFileDialog.openDialog("browse-sudoku", "Choisissez un fichier", ".json", ".");
|
||||||
|
}
|
||||||
|
renderFileDialog();
|
||||||
|
}
|
||||||
|
|
||||||
|
public MultiDoku getDoku() {
|
||||||
|
return doku;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
package gui.menu;
|
package gui.menu;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
import game.Player;
|
import game.Player;
|
||||||
import imgui.ImGui;
|
import imgui.ImGui;
|
||||||
import network.client.Client;
|
import network.client.Client;
|
||||||
import network.server.Server;
|
import network.server.Server;
|
||||||
|
import sudoku.constraint.Constraint;
|
||||||
import sudoku.structure.MultiDoku;
|
import sudoku.structure.MultiDoku;
|
||||||
import sudoku.structure.SudokuFactory;
|
import sudoku.structure.SudokuFactory;
|
||||||
|
|
||||||
@@ -17,7 +20,8 @@ public class MultiPlayerView extends BaseView {
|
|||||||
this.client = client;
|
this.client = client;
|
||||||
this.server = server;
|
this.server = server;
|
||||||
this.client.onDisconnect.connect(this::onDisconnect);
|
this.client.onDisconnect.connect(this::onDisconnect);
|
||||||
this.client.onGameStarted.connect(() -> this.stateMachine.pushState(new MultiPlayerDokuView(stateMachine, client, server)));
|
this.client.onGameStarted
|
||||||
|
.connect(() -> this.stateMachine.pushState(new MultiPlayerDokuView(stateMachine, client, server)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -36,7 +40,7 @@ public class MultiPlayerView extends BaseView {
|
|||||||
} else {
|
} else {
|
||||||
if (ImGui.button("Démarrer")) {
|
if (ImGui.button("Démarrer")) {
|
||||||
// temp
|
// temp
|
||||||
MultiDoku doku = SudokuFactory.createBasicXShapedMultidoku(3);
|
MultiDoku doku = SudokuFactory.createBasicXShapedMultidoku(3, Arrays.asList(Constraint.Diagonal));
|
||||||
this.server.startGame(doku);
|
this.server.startGame(doku);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,88 +1,26 @@
|
|||||||
package gui.menu;
|
package gui.menu;
|
||||||
|
|
||||||
|
import gui.SudokuSelector;
|
||||||
import imgui.ImGui;
|
import imgui.ImGui;
|
||||||
import imgui.type.ImInt;
|
|
||||||
import sudoku.structure.Difficulty;
|
|
||||||
import sudoku.structure.MultiDoku;
|
|
||||||
import sudoku.structure.SudokuFactory;
|
|
||||||
|
|
||||||
public class SoloMenu extends BaseView {
|
public class SoloMenu extends BaseView {
|
||||||
|
|
||||||
private final ImInt sudokuType = new ImInt(0);
|
private final SudokuSelector sudokuSelector;
|
||||||
|
|
||||||
private final ImInt difficulty = new ImInt(Difficulty.Medium.ordinal());
|
|
||||||
private final String[] difficulties;
|
|
||||||
|
|
||||||
private static final String[] sudokuTypes = { "Carré", "Rectangle", "Multidoku" };
|
|
||||||
private static final int SQUARE = 0, RECTANGLE = 1, MULTIDOKU = 2;
|
|
||||||
|
|
||||||
private final ImInt sudokuSize = new ImInt(3);
|
|
||||||
|
|
||||||
private final ImInt sudokuWidth = new ImInt(3);
|
|
||||||
private final ImInt sudokuHeight = new ImInt(3);
|
|
||||||
|
|
||||||
public SoloMenu(StateMachine stateMachine) {
|
public SoloMenu(StateMachine stateMachine) {
|
||||||
super(stateMachine);
|
super(stateMachine);
|
||||||
Difficulty[] diffs = Difficulty.values();
|
this.sudokuSelector = new SudokuSelector(true);
|
||||||
difficulties = new String[diffs.length];
|
this.sudokuSelector.onSelect.connect(this::pushSudokuState);
|
||||||
for (int i = 0; i < diffs.length; i++) {
|
|
||||||
difficulties[i] = diffs[i].getDisplayName();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void pushSudokuState(MultiDoku doku, boolean empty) {
|
private void pushSudokuState() {
|
||||||
if (!empty) {
|
this.stateMachine.pushState(new SudokuView(stateMachine, this.sudokuSelector.getDoku()));
|
||||||
try {
|
|
||||||
SudokuFactory.fillDoku(doku, Difficulty.values()[difficulty.get()]);
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.stateMachine.pushState(new SudokuView(stateMachine, doku));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render() {
|
public void render() {
|
||||||
ImGui.text("Solo");
|
ImGui.text("Solo");
|
||||||
ImGui.combo("Type de Sudoku", sudokuType, sudokuTypes);
|
sudokuSelector.render();
|
||||||
ImGui.combo("Difficulté", difficulty, difficulties);
|
|
||||||
switch (sudokuType.get()) {
|
|
||||||
case SQUARE:
|
|
||||||
ImGui.inputInt("Taille", sudokuSize);
|
|
||||||
if (ImGui.button("Résoudre un sudoku")) {
|
|
||||||
pushSudokuState(SudokuFactory.createBasicEmptySquareSudoku(sudokuSize.get()), false);
|
|
||||||
}
|
|
||||||
if (ImGui.button("Générer une grille vide")) {
|
|
||||||
pushSudokuState(SudokuFactory.createBasicEmptySquareSudoku(sudokuSize.get()), true);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case RECTANGLE:
|
|
||||||
ImGui.inputInt("Largeur", sudokuHeight);
|
|
||||||
ImGui.inputInt("Longueur", sudokuWidth);
|
|
||||||
if (ImGui.button("Résoudre un sudoku")) {
|
|
||||||
pushSudokuState(
|
|
||||||
SudokuFactory.createBasicEmptyRectangleSudoku(sudokuWidth.get(), sudokuHeight.get()),
|
|
||||||
false);
|
|
||||||
}
|
|
||||||
if (ImGui.button("Générer une grille vide")) {
|
|
||||||
pushSudokuState(
|
|
||||||
SudokuFactory.createBasicEmptyRectangleSudoku(sudokuWidth.get(), sudokuHeight.get()), true);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case MULTIDOKU:
|
|
||||||
ImGui.inputInt("Taille", sudokuSize);
|
|
||||||
if (ImGui.button("Résoudre un sudoku")) {
|
|
||||||
pushSudokuState(SudokuFactory.createBasicXShapedMultidoku(sudokuSize.get()), false);
|
|
||||||
}
|
|
||||||
if (ImGui.button("Générer une grille vide")) {
|
|
||||||
pushSudokuState(SudokuFactory.createBasicXShapedMultidoku(sudokuSize.get()), true);
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
renderReturnButton();
|
renderReturnButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import java.util.concurrent.CancellationException;
|
|||||||
import gui.SudokuRenderer;
|
import gui.SudokuRenderer;
|
||||||
import imgui.ImGui;
|
import imgui.ImGui;
|
||||||
import imgui.ImGuiStyle;
|
import imgui.ImGuiStyle;
|
||||||
|
import sudoku.io.SudokuSerializer;
|
||||||
import sudoku.solver.Solver;
|
import sudoku.solver.Solver;
|
||||||
import sudoku.structure.MultiDoku;
|
import sudoku.structure.MultiDoku;
|
||||||
|
|
||||||
@@ -14,11 +15,19 @@ public class SudokuView extends BaseView {
|
|||||||
private final SudokuRenderer sudokuRenderer;
|
private final SudokuRenderer sudokuRenderer;
|
||||||
private Thread resolveThread;
|
private Thread resolveThread;
|
||||||
private final MultiDoku doku;
|
private final MultiDoku doku;
|
||||||
|
private String lastSavePath = null;
|
||||||
|
|
||||||
|
private boolean resolved = false;
|
||||||
|
|
||||||
public SudokuView(StateMachine stateMachine, MultiDoku doku) {
|
public SudokuView(StateMachine stateMachine, MultiDoku doku) {
|
||||||
super(stateMachine);
|
super(stateMachine);
|
||||||
this.doku = doku;
|
this.doku = doku;
|
||||||
this.sudokuRenderer = new SudokuRenderer(doku);
|
this.sudokuRenderer = new SudokuRenderer(doku);
|
||||||
|
this.sudokuRenderer.onResolve.connect(this::onResolve);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onResolve() {
|
||||||
|
this.resolved = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void stopResolve() {
|
private void stopResolve() {
|
||||||
@@ -63,7 +72,7 @@ public class SudokuView extends BaseView {
|
|||||||
|
|
||||||
boolean beginSolve = false;
|
boolean beginSolve = false;
|
||||||
|
|
||||||
if (centeredButton("Résoudre")) {
|
if (!resolved && centeredButton("Résoudre")) {
|
||||||
beginSolve = true;
|
beginSolve = true;
|
||||||
}
|
}
|
||||||
if (resolveThread != null)
|
if (resolveThread != null)
|
||||||
@@ -81,12 +90,29 @@ public class SudokuView extends BaseView {
|
|||||||
stopResolve();
|
stopResolve();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (resolved) {
|
||||||
|
ImGui.text("Bravo !");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderSaveButton() {
|
||||||
|
if (ImGui.button("Sauvegarder l'état de la grille")) {
|
||||||
|
lastSavePath = SudokuSerializer.saveMultiDoku(doku);
|
||||||
|
ImGui.openPopup("saveDone");
|
||||||
|
}
|
||||||
|
if (ImGui.beginPopup("saveDone")) {
|
||||||
|
ImGui.text("Sudoku sauvegardé dans ");
|
||||||
|
ImGui.text(lastSavePath);
|
||||||
|
ImGui.endPopup();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render() {
|
public void render() {
|
||||||
sudokuRenderer.render();
|
sudokuRenderer.render();
|
||||||
renderSolveButton();
|
renderSolveButton();
|
||||||
|
renderSaveButton();
|
||||||
renderCancelButton();
|
renderCancelButton();
|
||||||
renderReturnButton();
|
renderReturnButton();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,30 +18,29 @@ public class Main {
|
|||||||
|
|
||||||
int blockWidth = 2;
|
int blockWidth = 2;
|
||||||
int blockHeight = 2;
|
int blockHeight = 2;
|
||||||
var multidoku = SudokuFactory.createBasicEmptyRectangleSudoku(blockWidth, blockHeight);
|
var multidoku = SudokuFactory.createBasicEmptyRectangleDoku(blockWidth, blockHeight,
|
||||||
|
SudokuFactory.DEFAULT_CONSTRAINTS);
|
||||||
var sudoku = multidoku.getSubGrid(0);
|
var sudoku = multidoku.getSubGrid(0);
|
||||||
if (!sudoku.setCellsSymbol(Arrays.asList(0, 1, 2, 3, 2, 3, 1, 1, 1, 0, 3, 2, 3, 2, 1, 1))) {
|
if (!sudoku.setCellsSymbol(Arrays.asList(0, 1, 2, 3, 2, 3, 1, 1, 1, 0, 3, 2, 3, 2, 1, 1))) {
|
||||||
System.out.println("At least one of those values does not respect the constraints.");
|
System.out.println("At least one of those values does not respect the constraints.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// sudoku.setCellSymbol(8,3,0);
|
// sudoku.setCellSymbol(8,3,0);
|
||||||
|
|
||||||
SudokuPrinter.printRectangleSudoku(multidoku.getSubGrid(0), blockWidth, blockHeight);
|
SudokuPrinter.printRectangleSudoku(multidoku.getSubGrid(0), blockWidth, blockHeight);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Solver solver = new Solver();
|
* Solver solver = new Solver();
|
||||||
ArrayList<IConstraint> constraints = new ArrayList<>();
|
* ArrayList<IConstraint> constraints = new ArrayList<>();
|
||||||
constraints.add(new LineConstraint());
|
* constraints.add(new LineConstraint());
|
||||||
constraints.add(new ColumnConstraint());
|
* constraints.add(new ColumnConstraint());
|
||||||
constraints.add(new BlockConstraint());
|
* constraints.add(new BlockConstraint());
|
||||||
try {
|
* try {
|
||||||
solver.solve(multidoku, constraints);
|
* solver.solve(multidoku, constraints);
|
||||||
} catch (Exception e) {
|
* } catch (Exception e) {
|
||||||
System.out.println(e);
|
* System.out.println(e);
|
||||||
}
|
* }
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
52
app/src/main/java/sudoku/constraint/Constraint.java
Normal file
52
app/src/main/java/sudoku/constraint/Constraint.java
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package sudoku.constraint;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import sudoku.structure.Sudoku;
|
||||||
|
|
||||||
|
public enum Constraint {
|
||||||
|
|
||||||
|
Block("Bloc", new BlockConstraint()),
|
||||||
|
Column("Colonne", new ColumnConstraint()),
|
||||||
|
Line("Ligne", new LineConstraint()),
|
||||||
|
Diagonal("Diagonale", new DiagonalConstraint());
|
||||||
|
|
||||||
|
String displayName;
|
||||||
|
IConstraint constraint;
|
||||||
|
|
||||||
|
private Constraint(String displayName, IConstraint contraint) {
|
||||||
|
this.constraint = contraint;
|
||||||
|
this.displayName = displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean canBePlaced(Sudoku s, int x, int y, int newValue) {
|
||||||
|
return getConstraint().canBePlaced(s, x, y, newValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Integer> getPossibleSymbols(final Sudoku s, int x, int y) {
|
||||||
|
return getConstraint().getPossibleSymbols(s, x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDisplayName() {
|
||||||
|
return displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IConstraint getConstraint() {
|
||||||
|
return constraint;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String[] constraintNames;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Constraint[] cons = Constraint.values();
|
||||||
|
constraintNames = new String[cons.length];
|
||||||
|
for (int i = 0; i < cons.length; i++) {
|
||||||
|
constraintNames[i] = cons[i].getDisplayName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String[] getConstraintNames() {
|
||||||
|
return constraintNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
24
app/src/main/java/sudoku/constraint/DiagonalConstraint.java
Normal file
24
app/src/main/java/sudoku/constraint/DiagonalConstraint.java
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package sudoku.constraint;
|
||||||
|
|
||||||
|
import sudoku.structure.Sudoku;
|
||||||
|
|
||||||
|
public class DiagonalConstraint implements IConstraint {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canBePlaced(Sudoku s, int x, int y, int newSymbolIndex) {
|
||||||
|
if (x == y) {
|
||||||
|
for (int i = 0; i < s.getSize(); i++) {
|
||||||
|
if (s.getCell(i, i).getSymbolIndex() == newSymbolIndex)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else if (s.getSize() - x == y) {
|
||||||
|
for (int i = 0; i < s.getSize(); i++) {
|
||||||
|
if (s.getCell(s.getSize() - i - 1, i).getSymbolIndex() == newSymbolIndex)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// not in diagonal
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ import java.util.List;
|
|||||||
import org.json.JSONArray;
|
import org.json.JSONArray;
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import sudoku.constraint.Constraint;
|
||||||
import sudoku.structure.Block;
|
import sudoku.structure.Block;
|
||||||
import sudoku.structure.Cell;
|
import sudoku.structure.Cell;
|
||||||
import sudoku.structure.MultiDoku;
|
import sudoku.structure.MultiDoku;
|
||||||
@@ -34,7 +35,6 @@ public class SudokuSerializer {
|
|||||||
for (Cell cell : sudoku.getCells()) {
|
for (Cell cell : sudoku.getCells()) {
|
||||||
if (!cellIds.contains(cell)) {
|
if (!cellIds.contains(cell)) {
|
||||||
cellIds.add(cell);
|
cellIds.add(cell);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Block block = cell.getBlock();
|
Block block = cell.getBlock();
|
||||||
@@ -74,6 +74,7 @@ public class SudokuSerializer {
|
|||||||
JSONObject jsonSudoku = new JSONObject();
|
JSONObject jsonSudoku = new JSONObject();
|
||||||
JSONArray cellsJsonArray = new JSONArray();
|
JSONArray cellsJsonArray = new JSONArray();
|
||||||
JSONArray blocksJsonArray = new JSONArray();
|
JSONArray blocksJsonArray = new JSONArray();
|
||||||
|
JSONArray constraintsJsonArray = new JSONArray();
|
||||||
|
|
||||||
Sudoku sudoku = multidoku.getSubGrid(i);
|
Sudoku sudoku = multidoku.getSubGrid(i);
|
||||||
|
|
||||||
@@ -91,6 +92,13 @@ public class SudokuSerializer {
|
|||||||
blocksJsonArray.put(blockID);
|
blocksJsonArray.put(blockID);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// serialize constraints
|
||||||
|
|
||||||
|
for (Constraint cons : sudoku.getConstraints()) {
|
||||||
|
constraintsJsonArray.put(cons.ordinal());
|
||||||
|
}
|
||||||
|
|
||||||
|
jsonSudoku.put("constraints", constraintsJsonArray);
|
||||||
jsonSudoku.put("cells", cellsJsonArray);
|
jsonSudoku.put("cells", cellsJsonArray);
|
||||||
jsonSudoku.put("blocks", blocksJsonArray);
|
jsonSudoku.put("blocks", blocksJsonArray);
|
||||||
jsonSudoku.put("blockWidth", sudoku.getBlockWidth());
|
jsonSudoku.put("blockWidth", sudoku.getBlockWidth());
|
||||||
@@ -107,9 +115,9 @@ public class SudokuSerializer {
|
|||||||
/**
|
/**
|
||||||
* Save a serialized MultiDoku in a JSON file.
|
* Save a serialized MultiDoku in a JSON file.
|
||||||
* @param doku MultiDoku, MultiDoku to save.
|
* @param doku MultiDoku, MultiDoku to save.
|
||||||
* @return int, number of the save.
|
* @return String, the path of the save.
|
||||||
*/
|
*/
|
||||||
public static int saveMultiDoku(final MultiDoku doku) {
|
public static String saveMultiDoku(final MultiDoku doku) {
|
||||||
|
|
||||||
JSONObject jsonRoot = serializeSudoku(doku);
|
JSONObject jsonRoot = serializeSudoku(doku);
|
||||||
|
|
||||||
@@ -124,9 +132,9 @@ public class SudokuSerializer {
|
|||||||
try (FileWriter file = new FileWriter(f)) {
|
try (FileWriter file = new FileWriter(f)) {
|
||||||
file.write(jsonRoot.toString(3));
|
file.write(jsonRoot.toString(3));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.fillInStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
return i;
|
return f.getAbsolutePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -212,9 +220,11 @@ public class SudokuSerializer {
|
|||||||
JSONObject sudokuJsonObject = multidokuJsonObject.getJSONObject(i);
|
JSONObject sudokuJsonObject = multidokuJsonObject.getJSONObject(i);
|
||||||
JSONArray sudokuCellsJsonArray = sudokuJsonObject.getJSONArray("cells");
|
JSONArray sudokuCellsJsonArray = sudokuJsonObject.getJSONArray("cells");
|
||||||
JSONArray sudokuBlocksJsonArray = sudokuJsonObject.getJSONArray("blocks");
|
JSONArray sudokuBlocksJsonArray = sudokuJsonObject.getJSONArray("blocks");
|
||||||
|
JSONArray sudokuConstraintsJsonArray = sudokuJsonObject.getJSONArray("constraints");
|
||||||
|
|
||||||
List<Cell> sudokuCells = new ArrayList<>();
|
List<Cell> sudokuCells = new ArrayList<>();
|
||||||
List<Block> sudokuBlocks = new ArrayList<>();
|
List<Block> sudokuBlocks = new ArrayList<>();
|
||||||
|
List<Constraint> sudokuConstraints = new ArrayList<>();
|
||||||
|
|
||||||
for (int j = 0; j < sudokuCellsJsonArray.length(); j++) {
|
for (int j = 0; j < sudokuCellsJsonArray.length(); j++) {
|
||||||
int cellID = sudokuCellsJsonArray.getInt(j);
|
int cellID = sudokuCellsJsonArray.getInt(j);
|
||||||
@@ -226,7 +236,12 @@ public class SudokuSerializer {
|
|||||||
sudokuBlocks.add(blocks.get(blockID));
|
sudokuBlocks.add(blocks.get(blockID));
|
||||||
}
|
}
|
||||||
|
|
||||||
Sudoku s = new Sudoku(sudokuCells, sudokuBlocks, SudokuFactory.DEFAULT_CONSTRAINTS);
|
for (int j = 0; j < sudokuConstraintsJsonArray.length(); j++) {
|
||||||
|
int constraintID = sudokuConstraintsJsonArray.getInt(j);
|
||||||
|
sudokuConstraints.add(Constraint.values()[constraintID]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Sudoku s = new Sudoku(sudokuCells, sudokuBlocks, sudokuConstraints);
|
||||||
s.setBlockWidth(sudokuJsonObject.getInt("blockWidth"));
|
s.setBlockWidth(sudokuJsonObject.getInt("blockWidth"));
|
||||||
sudokus.add(s);
|
sudokus.add(s);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,8 @@ import java.util.List;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Class qui représente les block de chaque sudoku,
|
* Class qui représente les block de chaque sudoku,
|
||||||
* Un block étant un ensemble de cellule avec une contrainte de block qui lui ait associé
|
* Un block étant un ensemble de cellule avec une contrainte de block qui lui
|
||||||
|
* ait associé
|
||||||
*/
|
*/
|
||||||
public class Block {
|
public class Block {
|
||||||
|
|
||||||
@@ -18,14 +19,15 @@ public class Block {
|
|||||||
* List de sudoku qui contiennent le block
|
* List de sudoku qui contiennent le block
|
||||||
* Pour un acces plus rapide aux sudokus
|
* Pour un acces plus rapide aux sudokus
|
||||||
*/
|
*/
|
||||||
private List<Sudoku> sudokus;
|
private final List<Sudoku> sudokus;
|
||||||
|
|
||||||
public Block(List<Cell> cells) {
|
public Block(List<Cell> cells) {
|
||||||
this.cells = cells;
|
this.cells = cells;
|
||||||
|
this.sudokus = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Block() {
|
public Block() {
|
||||||
this.cells = new ArrayList<>();
|
this(new ArrayList<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Cell> getCells() {
|
public List<Cell> getCells() {
|
||||||
@@ -34,6 +36,7 @@ public class Block {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Ajoute une Cell au Block
|
* Ajoute une Cell au Block
|
||||||
|
*
|
||||||
* @param newCell Cell, à ajouter
|
* @param newCell Cell, à ajouter
|
||||||
*/
|
*/
|
||||||
void addCell(Cell newCell) {
|
void addCell(Cell newCell) {
|
||||||
@@ -42,6 +45,7 @@ public class Block {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Cherche si le Block contient déjà un symbole donné.
|
* Cherche si le Block contient déjà un symbole donné.
|
||||||
|
*
|
||||||
* @param symbolIndex int, un index de symbole
|
* @param symbolIndex int, un index de symbole
|
||||||
* @return boolean, true s'il contient le symbole et false sinon
|
* @return boolean, true s'il contient le symbole et false sinon
|
||||||
*/
|
*/
|
||||||
@@ -65,8 +69,4 @@ public class Block {
|
|||||||
public List<Sudoku> getSudokus() {
|
public List<Sudoku> getSudokus() {
|
||||||
return sudokus;
|
return sudokus;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setSudokus(List<Sudoku> sudokus) {
|
|
||||||
this.sudokus = sudokus;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ public enum Difficulty {
|
|||||||
double factor;
|
double factor;
|
||||||
String displayName;
|
String displayName;
|
||||||
|
|
||||||
Difficulty(String displayName, double factor) {
|
private Difficulty(String displayName, double factor) {
|
||||||
this.factor = factor;
|
this.factor = factor;
|
||||||
this.displayName = displayName;
|
this.displayName = displayName;
|
||||||
}
|
}
|
||||||
@@ -21,4 +21,18 @@ public enum Difficulty {
|
|||||||
return factor;
|
return factor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final String[] difficultyNames;
|
||||||
|
|
||||||
|
static {
|
||||||
|
Difficulty[] diffs = Difficulty.values();
|
||||||
|
difficultyNames = new String[diffs.length];
|
||||||
|
for (int i = 0; i < diffs.length; i++) {
|
||||||
|
difficultyNames[i] = diffs[i].getDisplayName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String[] getDifficultyNames() {
|
||||||
|
return difficultyNames;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package sudoku.structure;
|
package sudoku.structure;
|
||||||
|
|
||||||
|
import sudoku.constraint.BlockConstraint;
|
||||||
|
import sudoku.constraint.Constraint;
|
||||||
import sudoku.constraint.IConstraint;
|
import sudoku.constraint.IConstraint;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -22,13 +24,13 @@ public class Sudoku {
|
|||||||
/**
|
/**
|
||||||
* Liste des contraintes (TODO) du Sudoku.
|
* Liste des contraintes (TODO) du Sudoku.
|
||||||
*/
|
*/
|
||||||
private final List<IConstraint> constraints;
|
private final List<Constraint> constraints;
|
||||||
/**
|
/**
|
||||||
* Largeur des Blocks s'ils sont rectangulaires, valant 0 si ce n'est pas le cas.
|
* Largeur des Blocks s'ils sont rectangulaires, valant 0 si ce n'est pas le cas.
|
||||||
*/
|
*/
|
||||||
private int blockWidth;
|
private int blockWidth;
|
||||||
|
|
||||||
public Sudoku(List<Cell> cells, List<Block> blocks, List<IConstraint> constraints) {
|
public Sudoku(List<Cell> cells, List<Block> blocks, List<Constraint> constraints) {
|
||||||
this.cells = cells;
|
this.cells = cells;
|
||||||
this.blocks = blocks;
|
this.blocks = blocks;
|
||||||
this.constraints = constraints;
|
this.constraints = constraints;
|
||||||
@@ -79,7 +81,7 @@ public class Sudoku {
|
|||||||
* @return boolean, true si on peut la placer et false sinon.
|
* @return boolean, true si on peut la placer et false sinon.
|
||||||
*/
|
*/
|
||||||
public boolean canBePlaced(int x, int y, int value) {
|
public boolean canBePlaced(int x, int y, int value) {
|
||||||
for (IConstraint constraint : this.constraints) {
|
for (Constraint constraint : this.constraints) {
|
||||||
if (!constraint.canBePlaced(this, x, y, value)) {
|
if (!constraint.canBePlaced(this, x, y, value)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -139,7 +141,7 @@ public class Sudoku {
|
|||||||
*/
|
*/
|
||||||
public Cell setCellSymbol(int x, int y, int value) {
|
public Cell setCellSymbol(int x, int y, int value) {
|
||||||
assert (isValidCoords(x, y));
|
assert (isValidCoords(x, y));
|
||||||
for (IConstraint constraint : this.constraints) {
|
for (Constraint constraint : this.constraints) {
|
||||||
if (!constraint.canBePlaced(this, x, y, value)) {
|
if (!constraint.canBePlaced(this, x, y, value)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -205,7 +207,7 @@ public class Sudoku {
|
|||||||
return this.cells.get(i);
|
return this.cells.get(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<IConstraint> getConstraints() {
|
public List<Constraint> getConstraints() {
|
||||||
return constraints;
|
return constraints;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,7 +267,7 @@ public class Sudoku {
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public void updateSymbolsPossibilities() {
|
public void updateSymbolsPossibilities() {
|
||||||
for (IConstraint constraint : constraints) {
|
for (Constraint constraint : constraints) {
|
||||||
List<Cell> cells = this.getCells();
|
List<Cell> cells = this.getCells();
|
||||||
for (Cell cell : cells) {
|
for (Cell cell : cells) {
|
||||||
Coordinate coord = null;
|
Coordinate coord = null;
|
||||||
@@ -275,7 +277,11 @@ public class Sudoku {
|
|||||||
System.out.println("Cas jamais atteint.");
|
System.out.println("Cas jamais atteint.");
|
||||||
}
|
}
|
||||||
List<Integer> newPossibleSymbols = cell.getPossibleSymbols();
|
List<Integer> newPossibleSymbols = cell.getPossibleSymbols();
|
||||||
newPossibleSymbols.retainAll(constraint.getPossibleSymbols(this, coord.getX(), coord.getY()));
|
newPossibleSymbols.retainAll(constraint.getPossibleSymbols(
|
||||||
|
this,
|
||||||
|
coord.getX(),
|
||||||
|
coord.getY()
|
||||||
|
));
|
||||||
|
|
||||||
cell.setPossibleSymbols(newPossibleSymbols);
|
cell.setPossibleSymbols(newPossibleSymbols);
|
||||||
}
|
}
|
||||||
@@ -323,7 +329,7 @@ public class Sudoku {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < this.constraints.size(); i++) {
|
for (int i = 0; i < this.constraints.size(); i++) {
|
||||||
IConstraint constraint = this.constraints.get(i);
|
Constraint constraint = this.constraints.get(i);
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
result.addAll(constraint.getPossibleSymbols(this, cellCoordinates.getX(), cellCoordinates.getY()));
|
result.addAll(constraint.getPossibleSymbols(this, cellCoordinates.getX(), cellCoordinates.getY()));
|
||||||
} else {
|
} else {
|
||||||
@@ -446,4 +452,8 @@ public class Sudoku {
|
|||||||
this.blockWidth = blockWidth;
|
this.blockWidth = blockWidth;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasConstraint(Constraint constraint) {
|
||||||
|
return this.constraints.contains(constraint);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
package sudoku.structure;
|
package sudoku.structure;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Paths;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
@@ -8,9 +11,10 @@ import java.util.Random;
|
|||||||
|
|
||||||
import sudoku.constraint.BlockConstraint;
|
import sudoku.constraint.BlockConstraint;
|
||||||
import sudoku.constraint.ColumnConstraint;
|
import sudoku.constraint.ColumnConstraint;
|
||||||
|
import sudoku.constraint.Constraint;
|
||||||
|
import sudoku.constraint.DiagonalConstraint;
|
||||||
import sudoku.constraint.IConstraint;
|
import sudoku.constraint.IConstraint;
|
||||||
import sudoku.constraint.LineConstraint;
|
import sudoku.constraint.LineConstraint;
|
||||||
import sudoku.io.SudokuPrinter;
|
|
||||||
import sudoku.solver.Solver;
|
import sudoku.solver.Solver;
|
||||||
|
|
||||||
public class SudokuFactory {
|
public class SudokuFactory {
|
||||||
@@ -19,18 +23,13 @@ public class SudokuFactory {
|
|||||||
* Générateur de nombre aléatoire.
|
* Générateur de nombre aléatoire.
|
||||||
*/
|
*/
|
||||||
private static final Random random = new Random();
|
private static final Random random = new Random();
|
||||||
/**
|
|
||||||
* Difficulté avec le ration des cases qui seront vides.
|
|
||||||
*/
|
|
||||||
private static final double VERY_EASY = 0.1;
|
|
||||||
private static final double EASY = 0.25;
|
|
||||||
private static final double MEDIUM = 0.5;
|
|
||||||
private static final double HARD = 0.75;
|
|
||||||
/**
|
/**
|
||||||
* Liste des contraintes par défaut d'un Multi- ou Sudoku.
|
* Liste des contraintes par défaut d'un Multi- ou Sudoku.
|
||||||
* Comprend les contraintes de blocs, de lignes, et de colonnes.
|
* Comprend les contraintes de blocs, de lignes, et de colonnes.
|
||||||
*/
|
*/
|
||||||
public static List<IConstraint> DEFAULT_CONSTRAINTS = Arrays.asList(new BlockConstraint(), new LineConstraint(), new ColumnConstraint());
|
public static List<Constraint> DEFAULT_CONSTRAINTS = Arrays.asList(Constraint.Block, Constraint.Column,
|
||||||
|
Constraint.Line);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Créée des Cells et les met dans une liste de taille size.
|
* Créée des Cells et les met dans une liste de taille size.
|
||||||
@@ -81,9 +80,9 @@ public class SudokuFactory {
|
|||||||
* @param heightBlock int, hauteur des Blocks.
|
* @param heightBlock int, hauteur des Blocks.
|
||||||
* @return MultiDoku, MultiDoku vide.
|
* @return MultiDoku, MultiDoku vide.
|
||||||
*/
|
*/
|
||||||
public static MultiDoku createBasicEmptyRectangleSudoku(int widthBlock, int heightBlock) {
|
public static MultiDoku createBasicEmptyRectangleDoku(int widthBlock, int heightBlock,
|
||||||
Sudoku s = createRectangleSudoku(widthBlock, heightBlock);
|
List<Constraint> constraints) {
|
||||||
return new MultiDoku(Arrays.asList(s));
|
return new MultiDoku(Arrays.asList(createRectangleSudoku(widthBlock, heightBlock, constraints)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -91,8 +90,8 @@ public class SudokuFactory {
|
|||||||
* @param size int, taille des Blocks.
|
* @param size int, taille des Blocks.
|
||||||
* @return MultiDoku, MultiDoku vide.
|
* @return MultiDoku, MultiDoku vide.
|
||||||
*/
|
*/
|
||||||
public static MultiDoku createBasicEmptySquareSudoku(int size) {
|
public static MultiDoku createBasicEmptySquareDoku(int size, List<Constraint> constraints) {
|
||||||
return createBasicEmptyRectangleSudoku(size, size);
|
return new MultiDoku(Arrays.asList(createSquareSudoku(size, constraints)));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -160,11 +159,14 @@ public class SudokuFactory {
|
|||||||
* @param heightBlock int, hauteur des Blocks.
|
* @param heightBlock int, hauteur des Blocks.
|
||||||
* @return Sudoku, Sudoku vide.
|
* @return Sudoku, Sudoku vide.
|
||||||
*/
|
*/
|
||||||
private static Sudoku createRectangleSudoku(int widthBlock, int heightBlock) {
|
private static Sudoku createRectangleSudoku(int widthBlock, int heightBlock, List<Constraint> constraints) {
|
||||||
int symbolCount = widthBlock * heightBlock;
|
int symbolCount = widthBlock * heightBlock;
|
||||||
List<Cell> cases = initCells(symbolCount);
|
List<Cell> cases = initCells(symbolCount);
|
||||||
List<Block> blocs = initRectangleBlocs(cases, widthBlock, heightBlock);
|
List<Block> blocs = initRectangleBlocs(cases, widthBlock, heightBlock);
|
||||||
Sudoku s = new Sudoku(cases, blocs, DEFAULT_CONSTRAINTS);
|
Sudoku s = new Sudoku(cases, blocs, constraints);
|
||||||
|
for (Block block : s.getBlocks()) {
|
||||||
|
block.getSudokus().add(s);
|
||||||
|
}
|
||||||
s.setBlockWidth(widthBlock);
|
s.setBlockWidth(widthBlock);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
@@ -174,8 +176,8 @@ public class SudokuFactory {
|
|||||||
* @param size int, taille des Blocks.
|
* @param size int, taille des Blocks.
|
||||||
* @return Sudoku, Sudoku vide.
|
* @return Sudoku, Sudoku vide.
|
||||||
*/
|
*/
|
||||||
private static Sudoku createSquareSudoku(int size) {
|
private static Sudoku createSquareSudoku(int size, List<Constraint> constraints) {
|
||||||
return createRectangleSudoku(size, size);
|
return createRectangleSudoku(size, size, constraints);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -199,6 +201,7 @@ public class SudokuFactory {
|
|||||||
|
|
||||||
// on remplace le bloc
|
// on remplace le bloc
|
||||||
sudoku2.getBlocks().set(block2Y * blockWidth + block2X, block1);
|
sudoku2.getBlocks().set(block2Y * blockWidth + block2X, block1);
|
||||||
|
block1.getSudokus().add(sudoku2);
|
||||||
|
|
||||||
// on remplace les cellules
|
// on remplace les cellules
|
||||||
for (int i = 0; i < block1.getCells().size(); i++) {
|
for (int i = 0; i < block1.getCells().size(); i++) {
|
||||||
@@ -218,7 +221,7 @@ public class SudokuFactory {
|
|||||||
* @param size int, largeur des Blocks unitraires des Sudokus à crééer.
|
* @param size int, largeur des Blocks unitraires des Sudokus à crééer.
|
||||||
* @return MultiDoku, MultiDoku de forme X.
|
* @return MultiDoku, MultiDoku de forme X.
|
||||||
*/
|
*/
|
||||||
public static MultiDoku createBasicXShapedMultidoku(int size) {
|
public static MultiDoku createBasicXShapedMultidoku(int size, List<Constraint> constraints) {
|
||||||
assert (size > 1);
|
assert (size > 1);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -227,11 +230,11 @@ public class SudokuFactory {
|
|||||||
* 4 5
|
* 4 5
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Sudoku sudoku1 = createSquareSudoku(size);
|
Sudoku sudoku1 = createSquareSudoku(size, constraints);
|
||||||
Sudoku sudoku2 = createSquareSudoku(size);
|
Sudoku sudoku2 = createSquareSudoku(size, constraints);
|
||||||
Sudoku sudoku3 = createSquareSudoku(size);
|
Sudoku sudoku3 = createSquareSudoku(size, constraints);
|
||||||
Sudoku sudoku4 = createSquareSudoku(size);
|
Sudoku sudoku4 = createSquareSudoku(size, constraints);
|
||||||
Sudoku sudoku5 = createSquareSudoku(size);
|
Sudoku sudoku5 = createSquareSudoku(size, constraints);
|
||||||
|
|
||||||
linkSquareSudokus(sudoku1, sudoku2, new Coordinate(1 - size, 1 - size));
|
linkSquareSudokus(sudoku1, sudoku2, new Coordinate(1 - size, 1 - size));
|
||||||
linkSquareSudokus(sudoku1, sudoku3, new Coordinate(size - 1, 1 - size));
|
linkSquareSudokus(sudoku1, sudoku3, new Coordinate(size - 1, 1 - size));
|
||||||
@@ -243,15 +246,22 @@ public class SudokuFactory {
|
|||||||
|
|
||||||
public static void fillDoku(MultiDoku doku, Difficulty difficulty) throws Exception {
|
public static void fillDoku(MultiDoku doku, Difficulty difficulty) throws Exception {
|
||||||
Solver.solveRandom(doku, random);
|
Solver.solveRandom(doku, random);
|
||||||
//SudokuPrinter.printRectangleSudoku(doku.getSubGrid(0), 3, 3);
|
|
||||||
int nbCellsToEmpty = (int) (difficulty.getFactor() * doku.getNbCells());
|
int nbCellsToEmpty = (int) (difficulty.getFactor() * doku.getNbCells());
|
||||||
//System.out.println(nbCellsToEmpty);
|
boolean successfull = newDokuFromFilledOne(doku, nbCellsToEmpty);
|
||||||
boolean successful = newDokuFromFilledOne(doku, nbCellsToEmpty);
|
if (!successfull) {
|
||||||
|
|
||||||
|
|
||||||
if (!successful) {
|
|
||||||
throw new Exception("Canno't create this doku with this difficulty");
|
throw new Exception("Canno't create this doku with this difficulty");
|
||||||
}
|
}
|
||||||
doku.setFilledCellsImmutable();
|
doku.setFilledCellsImmutable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static MultiDoku fromfile(String filePath) {
|
||||||
|
try {
|
||||||
|
String content = Files.readString(Paths.get(filePath));
|
||||||
|
MultiDoku doku = SudokuSerializer.deserializeSudoku(content);
|
||||||
|
return doku;
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,34 +1,36 @@
|
|||||||
package sudoku;
|
package sudoku;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import java.io.File;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import java.util.Random;
|
||||||
|
|
||||||
import org.json.JSONObject;
|
import org.json.JSONObject;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import sudoku.io.SudokuPrinter;
|
|
||||||
import sudoku.io.SudokuSerializer;
|
import sudoku.io.SudokuSerializer;
|
||||||
import sudoku.structure.MultiDoku;
|
import sudoku.structure.MultiDoku;
|
||||||
import sudoku.structure.SudokuFactory;
|
import sudoku.structure.SudokuFactory;
|
||||||
|
|
||||||
import java.util.Random;
|
|
||||||
|
|
||||||
public class SudokuSerializerTest {
|
public class SudokuSerializerTest {
|
||||||
|
|
||||||
void testSerializeWithSize(int blockWidth, int blockHeight) {
|
void testSerializeWithSize(int blockWidth, int blockHeight) {
|
||||||
var sudoku = SudokuFactory.createBasicEmptyRectangleSudoku(blockWidth, blockHeight);
|
var sudoku = SudokuFactory.createBasicEmptyRectangleDoku(blockWidth, blockHeight,
|
||||||
|
SudokuFactory.DEFAULT_CONSTRAINTS);
|
||||||
JSONObject data = SudokuSerializer.serializeSudoku(sudoku);
|
JSONObject data = SudokuSerializer.serializeSudoku(sudoku);
|
||||||
MultiDoku multiDoku = SudokuSerializer.deserializeSudoku(data);
|
MultiDoku multiDoku = SudokuSerializer.deserializeSudoku(data);
|
||||||
assert (data.toString().equals(SudokuSerializer.serializeSudoku(multiDoku).toString()));
|
assert (data.toString().equals(SudokuSerializer.serializeSudoku(multiDoku).toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void testSaveWithSize(int blockWidth, int blockHeight) {
|
void testSaveWithSize(int blockWidth, int blockHeight) {
|
||||||
MultiDoku doku = SudokuFactory.createBasicEmptyRectangleSudoku(blockWidth, blockHeight);
|
MultiDoku doku = SudokuFactory.createBasicEmptyRectangleDoku(blockWidth, blockHeight,
|
||||||
int saveNumber = SudokuSerializer.saveMultiDoku(doku);
|
SudokuFactory.DEFAULT_CONSTRAINTS);
|
||||||
|
String savePath = SudokuSerializer.saveMultiDoku(doku);
|
||||||
MultiDoku otherDoku = null;
|
MultiDoku otherDoku = null;
|
||||||
try {
|
try {
|
||||||
otherDoku = SudokuSerializer.getSavedMultiDoku(saveNumber);
|
otherDoku = SudokuFactory.fromfile(savePath);
|
||||||
assert (otherDoku != null);
|
assert (otherDoku != null);
|
||||||
|
// clean file after test
|
||||||
|
File fileToDelete = new File(savePath);
|
||||||
|
fileToDelete.delete();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
assert false;
|
assert false;
|
||||||
@@ -41,18 +43,15 @@ public class SudokuSerializerTest {
|
|||||||
Random r = new Random();
|
Random r = new Random();
|
||||||
int testCount = 5;
|
int testCount = 5;
|
||||||
for (int i = 0; i < testCount; i++) {
|
for (int i = 0; i < testCount; i++) {
|
||||||
int blockWidth = r.nextInt(20) + 1;
|
int blockWidth = r.nextInt(10) + 1;
|
||||||
int blockHeight = r.nextInt(20) + 1;
|
int blockHeight = r.nextInt(10) + 1;
|
||||||
testSerializeWithSize(blockWidth, blockHeight);
|
testSerializeWithSize(blockWidth, blockHeight);
|
||||||
}
|
}
|
||||||
for (int i = 0; i < testCount; i++) {
|
for (int i = 0; i < testCount; i++) {
|
||||||
int blockWidth = r.nextInt(20) + 1;
|
int blockWidth = r.nextInt(10) + 1;
|
||||||
int blockHeight = r.nextInt(20) + 1;
|
int blockHeight = r.nextInt(10) + 1;
|
||||||
testSaveWithSize(blockWidth, blockHeight);
|
testSaveWithSize(blockWidth, blockHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,8 @@ class SolverTest {
|
|||||||
void solveTest() {
|
void solveTest() {
|
||||||
Random rand = new Random();
|
Random rand = new Random();
|
||||||
|
|
||||||
MultiDoku dokuToTest = SudokuFactory.createBasicEmptySquareSudoku(3);
|
MultiDoku dokuToTest = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
|
||||||
MultiDoku dokuResult = SudokuFactory.createBasicEmptySquareSudoku(3);
|
MultiDoku dokuResult = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
|
||||||
|
|
||||||
Sudoku sudokuToTest = dokuToTest.getSubGrid(0);
|
Sudoku sudokuToTest = dokuToTest.getSubGrid(0);
|
||||||
Sudoku sudokuResult = dokuResult.getSubGrid(0);
|
Sudoku sudokuResult = dokuResult.getSubGrid(0);
|
||||||
@@ -35,10 +35,8 @@ class SolverTest {
|
|||||||
|
|
||||||
assert (sudokuToTest.setImmutableCellsSymbol(immutableCells));
|
assert (sudokuToTest.setImmutableCellsSymbol(immutableCells));
|
||||||
|
|
||||||
|
|
||||||
SudokuPrinter.printRectangleSudoku(dokuToTest.getSubGrid(0), 3, 3);
|
SudokuPrinter.printRectangleSudoku(dokuToTest.getSubGrid(0), 3, 3);
|
||||||
|
|
||||||
|
|
||||||
List<Integer> correctCells = List.of(7, 6, 0, 3, 4, 2, 8, 5, 1,
|
List<Integer> correctCells = List.of(7, 6, 0, 3, 4, 2, 8, 5, 1,
|
||||||
2, 3, 8, 1, 5, 6, 7, 0, 4,
|
2, 3, 8, 1, 5, 6, 7, 0, 4,
|
||||||
1, 4, 5, 8, 0, 7, 3, 2, 6,
|
1, 4, 5, 8, 0, 7, 3, 2, 6,
|
||||||
@@ -51,16 +49,13 @@ class SolverTest {
|
|||||||
|
|
||||||
sudokuResult.setCellsSymbol(correctCells);
|
sudokuResult.setCellsSymbol(correctCells);
|
||||||
|
|
||||||
|
|
||||||
System.out.println("\n****************************Doku Control\n");
|
System.out.println("\n****************************Doku Control\n");
|
||||||
SudokuPrinter.printRectangleSudoku(sudokuResult, 3, 3);
|
SudokuPrinter.printRectangleSudoku(sudokuResult, 3, 3);
|
||||||
|
|
||||||
|
|
||||||
assert(dokuResult.isSolved());
|
assert(dokuResult.isSolved());
|
||||||
|
|
||||||
Solver.solveRandom(dokuToTest, rand);
|
Solver.solveRandom(dokuToTest, rand);
|
||||||
|
|
||||||
|
|
||||||
System.out.println("\n****************************\nDoku solved");
|
System.out.println("\n****************************\nDoku solved");
|
||||||
SudokuPrinter.printRectangleSudoku(dokuToTest.getSubGrid(0), 3, 3);
|
SudokuPrinter.printRectangleSudoku(dokuToTest.getSubGrid(0), 3, 3);
|
||||||
|
|
||||||
@@ -69,7 +64,7 @@ class SolverTest {
|
|||||||
|
|
||||||
assert(dokuToTest.equals(dokuResult));
|
assert(dokuToTest.equals(dokuResult));
|
||||||
|
|
||||||
MultiDoku dokuToTest2 = SudokuFactory.createBasicEmptySquareSudoku(3);
|
MultiDoku dokuToTest2 = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
|
||||||
Sudoku sudokuToTest2 = dokuToTest2.getSubGrid(0);
|
Sudoku sudokuToTest2 = dokuToTest2.getSubGrid(0);
|
||||||
|
|
||||||
List<Integer> immutableCells2 = List.of(ns, ns, 0, ns, ns, 2, 8, ns, 1,
|
List<Integer> immutableCells2 = List.of(ns, ns, 0, ns, ns, 2, 8, ns, 1,
|
||||||
@@ -87,7 +82,7 @@ class SolverTest {
|
|||||||
|
|
||||||
assert (!isSolved);
|
assert (!isSolved);
|
||||||
|
|
||||||
MultiDoku dokuToTest3 = SudokuFactory.createBasicEmptySquareSudoku(3);
|
MultiDoku dokuToTest3 = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
|
||||||
|
|
||||||
Solver.solveRandom(dokuToTest3, rand);
|
Solver.solveRandom(dokuToTest3, rand);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user