4 Commits

Author SHA1 Message Date
1e67e7a9d4 fix renderable multidoku max coords
All checks were successful
Linux arm64 / Build (push) Successful in 29s
2025-02-02 00:37:07 +01:00
Melvyn
70eef1646d refactor
All checks were successful
Linux arm64 / Build (push) Successful in 31s
2025-02-02 00:16:27 +01:00
5dfe4382fe feat: render new sudoku types
All checks were successful
Linux arm64 / Build (push) Successful in 27s
2025-02-02 00:10:25 +01:00
Melvyn
059886c2a4 feat : createPlusMMultidoku
All checks were successful
Linux arm64 / Build (push) Successful in 30s
2025-02-02 00:02:47 +01:00
31 changed files with 290 additions and 402 deletions

View File

Before

Width:  |  Height:  |  Size: 399 KiB

After

Width:  |  Height:  |  Size: 399 KiB

View File

@@ -11,6 +11,8 @@ plugins {
id 'application' id 'application'
} }
project.ext.os = System.properties['os.name'].toLowerCase().split(" ")[0]
repositories { repositories {
// Use Maven Central for resolving dependencies. // Use Maven Central for resolving dependencies.
mavenCentral() mavenCentral()
@@ -20,17 +22,13 @@ 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:5.9.1'
implementation 'com.google.guava:guava:31.1-jre'
implementation 'org.json:json:20250107' implementation 'org.json:json:20250107'
implementation "io.github.spair:imgui-java-app:1.88.0" implementation "io.github.spair:imgui-java-app:1.88.0"
implementation "org.lwjgl:lwjgl-stb:3.3.4" implementation "org.lwjgl:lwjgl-stb:3.3.4"
implementation "org.lwjgl:lwjgl-stb::natives-linux" runtimeOnly "org.lwjgl:lwjgl-stb::natives-$os"
implementation "org.lwjgl:lwjgl-stb::natives-windows"
implementation "org.lwjgl:lwjgl-stb::natives-macos"
} }
application { application {
@@ -50,10 +48,6 @@ jar {
} }
} }
test {
useJUnitPlatform()
}
run { run {
enableAssertions = true enableAssertions = true
} }

View File

@@ -1,32 +0,0 @@
package gui;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
public class AssetManager {
public static byte[] getResource(String name) {
// we first search it in files
File f = new File(name);
if (f.exists()){
FileInputStream fis;
try {
fis = new FileInputStream(f);
return fis.readAllBytes();
} catch (IOException e) {
e.printStackTrace();
}
}
// then in the jar
InputStream is = ClassLoader.getSystemResourceAsStream(name);
try {
return is.readAllBytes();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}

View File

@@ -2,10 +2,15 @@ package gui;
import gui.constants.Fonts; import gui.constants.Fonts;
import gui.constants.Images; import gui.constants.Images;
import gui.constants.Symbols;
import gui.menu.MainMenu; import gui.menu.MainMenu;
import gui.menu.StateMachine; import gui.menu.StateMachine;
import imgui.app.Application; import imgui.app.Application;
import imgui.app.Configuration; import imgui.app.Configuration;
import sudoku.io.SudokuPrinter;
import sudoku.structure.Difficulty;
import sudoku.structure.MultiDoku;
import sudoku.structure.SudokuFactory;
public class Main extends Application { public class Main extends Application {
@@ -31,7 +36,7 @@ public class Main extends Application {
@Override @Override
protected void preRun() { protected void preRun() {
super.preRun(); super.preRun();
Images.reloadImages(); Images.loadImages();
} }
@Override @Override
@@ -41,6 +46,13 @@ public class Main extends Application {
} }
public static void main(String[] args) { public static void main(String[] args) {
launch(new Main()); MultiDoku doku = SudokuFactory.createBasicPlusShapedMultidoku(3, 3, SudokuFactory.DEFAULT_CONSTRAINTS);
try {
SudokuFactory.fillDoku(doku, Difficulty.Easy);
} catch (Exception e) {
throw new RuntimeException();
}
SudokuPrinter.printMultiDoku(doku, 3, 3, Symbols.Numbers);
//launch(new Main());
} }
} }

View File

@@ -90,29 +90,23 @@ public class RenderableMultidoku {
} }
private static Coordinate getMaxSudokuCoordinate(Map<Sudoku, Coordinate> sudokusOffset) { private static Coordinate getMaxSudokuCoordinate(Map<Sudoku, Coordinate> sudokusOffset) {
Coordinate maxCoordinate = null; int maxX = 0;
Sudoku maxSudoku = null; int maxY = 0;
float maxDistanceSquared = 0; Sudoku lastSudoku = null;
for (var entry : sudokusOffset.entrySet()) { for (var entry : sudokusOffset.entrySet()) {
Coordinate coordinate = entry.getValue(); Coordinate coordinate = entry.getValue();
float distanceSquared = coordinate.getX() * coordinate.getX() + coordinate.getY() * coordinate.getY(); if (coordinate.getX() > maxX)
if (maxCoordinate == null) { maxX = coordinate.getX();
maxCoordinate = coordinate; if (coordinate.getY() > maxY)
maxDistanceSquared = distanceSquared; maxY = coordinate.getY();
maxSudoku = entry.getKey(); lastSudoku = entry.getKey();
} }
if (distanceSquared > maxDistanceSquared) { Coordinate maxCoordinate = new Coordinate(maxX, maxY);
maxDistanceSquared = distanceSquared; // tous les sudokus sont censés faire la même taille
maxSudoku = entry.getKey(); int sudokuSize = lastSudoku.getSize();
maxCoordinate = coordinate;
}
}
int blockWidth = maxSudoku.getBlockWidth(); return new Coordinate(maxCoordinate.getX() + sudokuSize, maxCoordinate.getY() + sudokuSize);
int blockHeight = maxSudoku.getSize() / blockWidth;
return new Coordinate(maxCoordinate.getX() + maxSudoku.getSize(), maxCoordinate.getY() + maxSudoku.getSize());
} }
public static RenderableMultidoku fromMultidoku(MultiDoku doku) { public static RenderableMultidoku fromMultidoku(MultiDoku doku) {

View File

@@ -1,6 +1,5 @@
package gui.constants; package gui.constants;
import gui.AssetManager;
import imgui.ImFont; import imgui.ImFont;
import imgui.ImFontConfig; import imgui.ImFontConfig;
import imgui.ImFontGlyphRangesBuilder; import imgui.ImFontGlyphRangesBuilder;
@@ -21,15 +20,15 @@ public class Fonts {
ImFontGlyphRangesBuilder builder = new ImFontGlyphRangesBuilder(); ImFontGlyphRangesBuilder builder = new ImFontGlyphRangesBuilder();
builder.addRanges(ImGui.getIO().getFonts().getGlyphRangesDefault()); builder.addRanges(ImGui.getIO().getFonts().getGlyphRangesDefault());
builder.addRanges(ImGui.getIO().getFonts().getGlyphRangesCyrillic()); builder.addRanges(ImGui.getIO().getFonts().getGlyphRangesCyrillic());
// builder.addRanges(ImGui.getIO().getFonts().getGlyphRangesChineseFull());
ImFontConfig cfg = new ImFontConfig(); ImFontConfig cfg = new ImFontConfig();
cfg.setGlyphRanges(builder.buildRanges()); cfg.setGlyphRanges(builder.buildRanges());
COMIC = ImGui.getIO().getFonts().addFontFromMemoryTTF(AssetManager.getResource("comic.ttf"), 50.0f); COMIC = ImGui.getIO().getFonts().addFontFromFileTTF(baseDir + "comic.ttf", 50.0f);
ARIAL_BOLD = ImGui.getIO().getFonts().addFontFromMemoryTTF(AssetManager.getResource("arial_bold.ttf"), 50.0f); ARIAL_BOLD = ImGui.getIO().getFonts().addFontFromFileTTF(baseDir + "arial_bold.ttf", 50.0f);
ARIAL = ImGui.getIO().getFonts().addFontFromMemoryTTF(AssetManager.getResource("arial.ttf"), 50.0f, cfg); ARIAL = ImGui.getIO().getFonts().addFontFromFileTTF(baseDir + "arial.ttf", 50.0f, cfg);
CHERI = ImGui.getIO().getFonts().addFontFromMemoryTTF(AssetManager.getResource("cheri.ttf"), 50.0f); CHERI = ImGui.getIO().getFonts().addFontFromFileTTF(baseDir + "cheri.ttf", 50.0f);
INFECTED = ImGui.getIO().getFonts().addFontFromMemoryTTF(AssetManager.getResource("INFECTED.ttf"), 50.0f); INFECTED = ImGui.getIO().getFonts().addFontFromFileTTF(baseDir + "INFECTED.ttf", 50.0f);
} }
} }

View File

@@ -5,21 +5,16 @@ import java.nio.ByteBuffer;
import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL11;
import org.lwjgl.stb.STBImage; import org.lwjgl.stb.STBImage;
import gui.AssetManager;
public class Images { public class Images {
public static int BACKGROUND; public static int BACKGROUND;
private static int loadTexture(byte[] imageData) { private static int loadTexture(String fileName) {
int[] width = new int[1]; int[] width = new int[1];
int[] height = new int[1]; int[] height = new int[1];
int[] channelCount = new int[1]; int[] channelCount = new int[1];
ByteBuffer img = ByteBuffer.allocateDirect(imageData.length); ByteBuffer pixels = STBImage.stbi_load(fileName, width, height, channelCount, 4);
img.put(imageData);
img.flip();
ByteBuffer pixels = STBImage.stbi_load_from_memory(img, width, height, channelCount, 4);
int textureID = GL11.glGenTextures(); int textureID = GL11.glGenTextures();
GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureID); GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureID);
@@ -37,8 +32,8 @@ public class Images {
return textureID; return textureID;
} }
public static void reloadImages() { public static void loadImages() {
BACKGROUND = loadTexture(AssetManager.getResource(Options.BackgroundPath)); BACKGROUND = loadTexture("background.png");
} }
} }

View File

@@ -4,6 +4,5 @@ public class Options {
public static Symbols Symboles = Symbols.Numbers; public static Symbols Symboles = Symbols.Numbers;
public static float BackgroundSpeed = 1.0f; public static float BackgroundSpeed = 1.0f;
public static String BackgroundPath = "background.png";
} }

View File

@@ -14,10 +14,14 @@ public enum SudokuType {
(constraints, params) -> SudokuFactory.createBasicEmptyRectangleDoku(params[0], params[1], constraints)), (constraints, params) -> SudokuFactory.createBasicEmptyRectangleDoku(params[0], params[1], constraints)),
RandomBloc("Blocs aléatoires", 1, RandomBloc("Blocs aléatoires", 1,
(constraints, params) -> SudokuFactory.createBasicEmptyRandomBlockDoku(params[0], constraints)), (constraints, params) -> SudokuFactory.createBasicEmptyRandomBlockDoku(params[0], constraints)),
MultiDokuSquare("Multidoku carré (X)", 1, MultiDokuXSquare("Multidoku carré (X)", 1,
(constraints, params) -> SudokuFactory.createBasicXShapedMultidoku(params[0], constraints)), (constraints, params) -> SudokuFactory.createBasicXShapedMultidoku(params[0], constraints)),
MultidokuRectangle("Multidoku rectangle (X)", 2, MultidokuXRectangle("Multidoku rectangle (X)", 2,
(constraints, params) -> SudokuFactory.createBasicXShapedMultidoku(params[0], params[1], constraints)); (constraints, params) -> SudokuFactory.createBasicXShapedMultidoku(params[0], params[1], constraints)),
MultiDokuPSquare("Multidoku carré (+)", 1,
(constraints, params) -> SudokuFactory.createBasicPlusShapedMultidoku(params[0], constraints)),
MultiDokuPRectangle("Multidoku rectangle (+)", 2,
(constraints, params) -> SudokuFactory.createBasicPlusShapedMultidoku(params[0], params[1], constraints));
String displayName; String displayName;
SudokuMaker maker; SudokuMaker maker;

View File

@@ -14,12 +14,12 @@ public class ConnexionStatusView extends BaseView {
private String displayText = "Connecting ..."; private String displayText = "Connecting ...";
public ConnexionStatusView(StateMachine stateMachine, String pseudo, String address, short port) public ConnexionStatusView(StateMachine stateMachine, String address, short port)
throws UnknownHostException, IOException { throws UnknownHostException, IOException {
super(stateMachine); super(stateMachine);
Thread t = new Thread(() -> { Thread t = new Thread(() -> {
try { try {
this.client = new Client(pseudo, address, port); this.client = new Client(address, port);
bindListeners(); bindListeners();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
@@ -29,13 +29,12 @@ public class ConnexionStatusView extends BaseView {
t.start(); t.start();
} }
public ConnexionStatusView(StateMachine stateMachine, String pseudo, short port) public ConnexionStatusView(StateMachine stateMachine, short port) throws UnknownHostException, IOException {
throws UnknownHostException, IOException {
super(stateMachine); super(stateMachine);
Thread t = new Thread(() -> { Thread t = new Thread(() -> {
try { try {
this.server = new Server(port); this.server = new Server(port);
this.client = new Client(pseudo, "localhost", port); this.client = new Client("localhost", port);
bindListeners(); bindListeners();
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();

View File

@@ -1,7 +1,6 @@
package gui.menu; package gui.menu;
import java.io.IOException; import java.io.IOException;
import java.util.Random;
import imgui.ImGui; import imgui.ImGui;
import imgui.ImVec2; import imgui.ImVec2;
@@ -12,7 +11,6 @@ public class MultiMenu extends BaseView {
private final ImInt port = new ImInt(25565); private final ImInt port = new ImInt(25565);
private final ImString address = new ImString("localhost"); private final ImString address = new ImString("localhost");
private final ImString pseudo = new ImString("Joueur" + new Random().nextInt());
public MultiMenu(StateMachine stateMachine) { public MultiMenu(StateMachine stateMachine) {
super(stateMachine); super(stateMachine);
@@ -24,10 +22,9 @@ public class MultiMenu extends BaseView {
ImGui.beginChild("##CreateGame", new ImVec2(displaySize.x / 2.0f, displaySize.y * 8.0f / 9.0f)); ImGui.beginChild("##CreateGame", new ImVec2(displaySize.x / 2.0f, displaySize.y * 8.0f / 9.0f));
if (ImGui.inputInt("Port", port)) if (ImGui.inputInt("Port", port))
port.set(Math.clamp(port.get(), 1, 65535)); port.set(Math.clamp(port.get(), 1, 65535));
ImGui.inputText("Pseudo", pseudo);
if (ImGui.button("Créer")) { if (ImGui.button("Créer")) {
try { try {
this.stateMachine.pushState(new ConnexionStatusView(stateMachine, pseudo.get(), (short) port.get())); this.stateMachine.pushState(new ConnexionStatusView(stateMachine, (short) port.get()));
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
} }
@@ -41,7 +38,6 @@ public class MultiMenu extends BaseView {
ImGui.inputText("Adresse", address); ImGui.inputText("Adresse", address);
if (ImGui.inputInt("Port", port)) if (ImGui.inputInt("Port", port))
port.set(Math.clamp(port.get(), 1, 65535)); port.set(Math.clamp(port.get(), 1, 65535));
ImGui.inputText("Pseudo", pseudo);
if (ImGui.button("Rejoindre")) { if (ImGui.button("Rejoindre")) {
try { try {
this.stateMachine.pushState(new ConnexionStatusView(stateMachine, address.get(), (short) port.get())); this.stateMachine.pushState(new ConnexionStatusView(stateMachine, address.get(), (short) port.get()));

View File

@@ -78,9 +78,4 @@ public class MultiPlayerView extends BaseView {
renderGameStatus(); renderGameStatus();
} }
@Override
public void cleanResources() {
this.selector.clean();
}
} }

View File

@@ -1,56 +1,28 @@
package gui.menu; package gui.menu;
import gui.constants.Images;
import gui.constants.Options; import gui.constants.Options;
import gui.constants.Symbols; import gui.constants.Symbols;
import imgui.ImGui; import imgui.ImGui;
import imgui.extension.imguifiledialog.ImGuiFileDialog;
import imgui.extension.imguifiledialog.flag.ImGuiFileDialogFlags;
import imgui.type.ImInt; import imgui.type.ImInt;
public class OptionsMenu extends BaseView { public class OptionsMenu extends BaseView {
private ImInt currentValue = new ImInt(); private ImInt currentValue = new ImInt();
private float backgroundSpeed[] = new float[] { Options.BackgroundSpeed }; private float backgroundSpeed[] = new float[]{Options.BackgroundSpeed};
public OptionsMenu(StateMachine stateMachine) { public OptionsMenu(StateMachine stateMachine) {
super(stateMachine); super(stateMachine);
} }
private void renderImageSelectDialog() {
if (ImGuiFileDialog.display("browse-img", ImGuiFileDialogFlags.None)) {
if (ImGuiFileDialog.isOk()) {
var selection = ImGuiFileDialog.getSelection();
for (var entry : selection.entrySet()) {
try {
String filePath = entry.getValue();
Options.BackgroundPath = filePath;
Images.reloadImages();
} catch (Exception e) {
e.printStackTrace();
}
}
}
ImGuiFileDialog.close();
}
}
private void renderImageSelectButton() {
if (ImGui.button("Changer de fond d'écran"))
ImGuiFileDialog.openDialog("browse-img", "Choisissez un fichier", ".png,.jpg,.jpeg", ".");
renderImageSelectDialog();
}
@Override @Override
public void render() { public void render() {
ImGui.text("Options"); ImGui.text("Options");
if (ImGui.combo("Jeu de symboles", currentValue, Symbols.getSymbolsNames())) { if(ImGui.combo("Jeu de symboles", currentValue, Symbols.getSymbolsNames())){
Options.Symboles = Symbols.values()[currentValue.get()]; Options.Symboles = Symbols.values()[currentValue.get()];
} }
if (ImGui.sliderFloat("Vitesse d'animation de l'arrière plan", backgroundSpeed, 0.0f, 10.0f)) { if(ImGui.sliderFloat("Vitesse d'animation de l'arrière plan", backgroundSpeed, 0.0f, 10.0f)){
Options.BackgroundSpeed = backgroundSpeed[0]; Options.BackgroundSpeed = backgroundSpeed[0];
} }
renderImageSelectButton();
renderReturnButton(); renderReturnButton();
} }

View File

@@ -25,9 +25,4 @@ public class SoloMenu extends BaseView {
renderReturnButton(); renderReturnButton();
} }
@Override
public void cleanResources() {
this.sudokuSelector.clean();
}
} }

View File

@@ -92,7 +92,6 @@ public class SudokuView extends BaseView {
} }
private void startSolve(Solver solver) { private void startSolve(Solver solver) {
this.doku.clearMutableCells();
resolveThread = new Thread(() -> { resolveThread = new Thread(() -> {
List<SolverStep> steps = new ArrayList<>(); List<SolverStep> steps = new ArrayList<>();
try { try {
@@ -159,8 +158,6 @@ public class SudokuView extends BaseView {
private void renderClearButton() { private void renderClearButton() {
if (centeredButton("Effacer")) { if (centeredButton("Effacer")) {
this.doku.clearMutableCells(); this.doku.clearMutableCells();
this.resolved = false;
this.unresolved = false;
} }
} }

View File

@@ -9,22 +9,13 @@ public class SmoothProgressBar {
private final float speed = 2.0f; private final float speed = 2.0f;
private final float clipConstant = 0.001f; private final float clipConstant = 0.001f;
private void updateProgress(float newProgress) { public void render(String label, ImVec2 size, float progress) {
float delta = newProgress - lastProgress; float delta = progress - lastProgress;
if (Math.abs(delta) < clipConstant) if (Math.abs(delta) < clipConstant)
lastProgress = newProgress; lastProgress = progress;
else else
lastProgress = lastProgress + delta * ImGui.getIO().getDeltaTime() * speed; lastProgress = lastProgress + delta * ImGui.getIO().getDeltaTime() * speed;
}
public void render(String label, ImVec2 size, float progress) {
updateProgress(progress);
ImGui.progressBar(lastProgress, size, label); ImGui.progressBar(lastProgress, size, label);
} }
public void render(float progress) {
updateProgress(progress);
ImGui.progressBar(lastProgress);
}
} }

View File

@@ -35,15 +35,10 @@ public class SudokuSelector {
private final String confirmMessage; private final String confirmMessage;
private Thread genThread = null;
private final SmoothProgressBar genProgressBar;
public SudokuSelector(boolean canGenEmptyGrid, String confirmMessage) { public SudokuSelector(boolean canGenEmptyGrid, String confirmMessage) {
this.canGenEmptyGrid = canGenEmptyGrid; this.canGenEmptyGrid = canGenEmptyGrid;
this.confirmMessage = confirmMessage; this.confirmMessage = confirmMessage;
initConstraints(); initConstraints();
this.genProgressBar = new SmoothProgressBar();
} }
private List<IConstraint> getConstraints() { private List<IConstraint> getConstraints() {
@@ -61,39 +56,16 @@ public class SudokuSelector {
} }
} }
private void stopGenThread() {
if (this.genThread != null) {
this.genThread.interrupt();
this.genThread = null;
}
}
private void renderGenProgress() {
if (ImGui.beginPopup("genProgress")) {
ImGui.text("Chargement de la grille ...");
int filled = this.doku.getFilledCells().size();
int total = this.doku.getCells().size();
this.genProgressBar.render(filled / (float) total);
ImGui.endPopup();
} else {
stopGenThread();
}
}
private void selectSudoku(MultiDoku doku, boolean empty) { private void selectSudoku(MultiDoku doku, boolean empty) {
this.doku = doku; this.doku = doku;
ImGui.openPopup("genProgress");
this.genThread = new Thread(() -> {
try {
if (!empty) { if (!empty) {
try {
SudokuFactory.fillDoku(doku, Difficulty.values()[difficulty.get()]); SudokuFactory.fillDoku(doku, Difficulty.values()[difficulty.get()]);
}
this.onSelect.emit(this.doku);
} catch (Exception e) { } catch (Exception e) {
e.printStackTrace(); e.printStackTrace();
} }
}); }
this.genThread.start(); this.onSelect.emit(this.doku);
} }
public void renderFileDialog() { public void renderFileDialog() {
@@ -159,12 +131,7 @@ public class SudokuSelector {
if (ImGui.button("À partir d'un fichier")) { if (ImGui.button("À partir d'un fichier")) {
ImGuiFileDialog.openDialog("browse-sudoku", "Choisissez un fichier", ".json", "."); ImGuiFileDialog.openDialog("browse-sudoku", "Choisissez un fichier", ".json", ".");
} }
renderGenProgress();
renderFileDialog(); renderFileDialog();
} }
public void clean() {
stopGenThread();
}
} }

View File

@@ -28,10 +28,12 @@ public class Client {
String disconnectReason = null; String disconnectReason = null;
public Client(String pseudo, String address, short port) throws UnknownHostException, IOException { public Client(String address, short port) throws UnknownHostException, IOException {
this.clientConnection = new ClientConnexion(address, port, this); this.clientConnection = new ClientConnexion(address, port, this);
this.game = new Game(); this.game = new Game();
login(pseudo); // temp
Random r = new Random();
login("Player" + r.nextInt());
} }
public void login(String pseudo) { public void login(String pseudo) {

View File

@@ -227,7 +227,7 @@ public class ConsoleInterface {
saveMultiDoku(doku); saveMultiDoku(doku);
break; break;
case "solution": case "solution":
solve(doku, listSymbols, width, height); solve(doku);
break; break;
case "exit": case "exit":
exit(); exit();
@@ -238,50 +238,19 @@ public class ConsoleInterface {
} }
} }
private void applyStep(SolverStep step) { private void solve(MultiDoku doku){
step.getCell().setSymbolIndex(step.getNewValue());
}
private boolean showStep(MultiDoku doku, List<String> listSymbols, int width, int height, SolverStep step) {
System.out.println("Here is the step : \n");
showMultidoku(doku, listSymbols, width, height);
applyStep(step);
System.out.println("\nTurns into :\n");
showMultidoku(doku, listSymbols, width, height);
System.out.println("Do you want to see the next step ? (y/n, default n)");
return reader.next().equals("y");
}
private void showSolveSteps(MultiDoku doku, List<String> listSymbols, int width, int height, List<SolverStep> steps) {
System.out.println("Would you like to see the steps of the solver ? (y/n, default n)");
doku.getStateManager().popState();
switch (reader.next()) {
case "y":
int stepCount = 0;
while(stepCount < steps.size() && showStep(doku, listSymbols, width, height, steps.get(stepCount))){stepCount++;}
break;
default:
break;
}
}
private void solve(MultiDoku doku, List<String> listSymbols, int width, int height){
System.out.println("Pick a solver to use : random ('random', default), human ('human') or mixed solver ('mixed')."); System.out.println("Pick a solver to use : random ('random', default), human ('human') or mixed solver ('mixed').");
List<SolverStep> steps = new ArrayList<>();
doku.getStateManager().pushState();
switch (reader.next()) { switch (reader.next()) {
case "human": case "human":
new HumanSolver().solve(doku, steps); new HumanSolver().solve(doku);
break; break;
case "mixed": case "mixed":
new MixedSolver().solve(doku, steps); new MixedSolver().solve(doku);
break; break;
default: default:
new RandomSolver().solve(doku, steps); new RandomSolver().solve(doku);
break; break;
} }
showSolveSteps(doku, listSymbols, width, height, steps);
} }
private void play(MultiDoku doku, List<String> listSymbols, int width, int height) { private void play(MultiDoku doku, List<String> listSymbols, int width, int height) {

View File

@@ -1,9 +1,14 @@
package sudoku.solver; package sudoku.solver;
import java.util.List; import java.util.List;
import java.util.concurrent.CancellationException;
import java.util.logging.Level;
import gui.constants.Symbols;
import sudoku.io.SudokuPrinter;
import sudoku.structure.Cell; import sudoku.structure.Cell;
import sudoku.structure.MultiDoku; import sudoku.structure.MultiDoku;
import sudoku.structure.Sudoku;
public class HumanSolver implements Solver { public class HumanSolver implements Solver {
@@ -15,25 +20,39 @@ public class HumanSolver implements Solver {
*/ */
@Override @Override
public boolean solve(MultiDoku doku, List<SolverStep> steps) { public boolean solve(MultiDoku doku, List<SolverStep> steps) {
while (!doku.isSolved()) { if (Thread.interrupted())
boolean filledCell = false; throw new CancellationException("User wants to stop the solver");
for (Cell cell : doku.getCells()) {
if (!cell.isMutable() || !cell.isEmpty())
continue;
List<Integer> possibleSymbols = cell.getPossibleSymbols(); Sudoku sudoku = doku.getSubGrid(0);
if (possibleSymbols.size() == 1) { logger.log(Level.FINE,
cell.setSymbolIndex(possibleSymbols.getFirst()); '\n' + SudokuPrinter.toStringRectangleSudoku(sudoku,
addStep(cell, steps); sudoku.getBlockWidth() == 0 ? sudoku.getSize() : sudoku.getBlockWidth(),
filledCell = true; sudoku.getBlockWidth() == 0 ? sudoku.getSize() : sudoku.getSize() / sudoku.getBlockWidth(),
} Symbols.Numbers));
}
// on ne peut plus remplir de cases, on abandonne
if (!filledCell)
return false;
}
if (doku.isSolved()) {
return true; return true;
} }
List<Cell> cellsToFill = doku.getEmptyCells();
if (cellsToFill.isEmpty()) {
return false;
}
for (Cell cellToFill : cellsToFill) {
List<Integer> possibleSymbols = cellToFill.getPossibleSymbols();
if (possibleSymbols.size() != 1) {
continue;
}
cellToFill.setSymbolIndex(possibleSymbols.getFirst());
addStep(cellToFill, steps);
return this.solve(doku, steps);
}
return doku.isSolved();
}
} }

View File

@@ -1,26 +1,18 @@
package sudoku.solver; package sudoku.solver;
import java.util.List; import java.util.List;
import java.util.Random;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
import java.util.logging.Level;
import gui.constants.Symbols;
import sudoku.io.SudokuPrinter;
import sudoku.structure.Cell; import sudoku.structure.Cell;
import sudoku.structure.MultiDoku; import sudoku.structure.MultiDoku;
import sudoku.structure.Sudoku;
public class MixedSolver implements Solver { public class MixedSolver implements Solver {
private Cell findCellToBacktrack(MultiDoku doku, int maxPossibilities) {
for (Cell cell : doku.getCells()) {
if (!cell.isMutable() || !cell.isEmpty())
continue;
List<Integer> possibleSymbols = cell.getPossibleSymbols();
if (possibleSymbols.size() == maxPossibilities) {
return cell;
}
}
return null;
}
/** /**
* Résout le MultiDoku passé en paramètre, avec règles de déduction et * Résout le MultiDoku passé en paramètre, avec règles de déduction et
* backtracking. * backtracking.
@@ -30,45 +22,55 @@ public class MixedSolver implements Solver {
*/ */
@Override @Override
public boolean solve(MultiDoku doku, List<SolverStep> steps) { public boolean solve(MultiDoku doku, List<SolverStep> steps) {
Random rand = new Random();
if (Thread.interrupted()) { if (Thread.interrupted()) {
throw new CancellationException("User wants to stop the solver"); throw new CancellationException("User wants to stop the solver");
} }
while (!doku.isSolved()) { Sudoku sudoku = doku.getSubGrid(0);
boolean filledCell = false; logger.log(Level.FINE,
for (Cell cell : doku.getCells()) { '\n' + SudokuPrinter.toStringRectangleSudoku(
if (!cell.isMutable() || !cell.isEmpty()) sudoku,
continue; sudoku.getBlockWidth() == 0 ? sudoku.getSize() : sudoku.getBlockWidth(),
sudoku.getBlockWidth() == 0 ? sudoku.getSize() : sudoku.getSize() / sudoku.getBlockWidth(),
Symbols.Numbers));
if (doku.isSolved()) {
return true;
}
Cell cellToFill = doku.getFirstEmptyCell();
if (cellToFill == null) {
return false;
}
List<Integer> possibleSymbols = cellToFill.getPossibleSymbols();
List<Integer> possibleSymbols = cell.getPossibleSymbols();
if (possibleSymbols.size() == 1) { if (possibleSymbols.size() == 1) {
cell.setSymbolIndex(possibleSymbols.getFirst()); cellToFill.setSymbolIndex(possibleSymbols.getFirst());
addStep(cell, steps); addStep(cellToFill, steps);
filledCell = true; if (this.solve(doku, steps)) {
}
}
// on ne peut plus remplir de cases, on tente de backtrack
if (!filledCell) {
int maxPossibilities = 2;
Cell backtrackCell = null;
while (backtrackCell == null) {
backtrackCell = findCellToBacktrack(doku, maxPossibilities);
maxPossibilities++;
}
// on fait du backtracking
List<Integer> possibilities = backtrackCell.getPossibleSymbols();
for (int symbol : possibilities) {
doku.getStateManager().pushState();
backtrackCell.setSymbolIndex(symbol);
if (solve(doku, steps))
return true; return true;
doku.getStateManager().popState();
} }
} }
while (!possibleSymbols.isEmpty()) {
int nextPossibleSymbolIndex = rand.nextInt(possibleSymbols.size());
int nextSymbol = possibleSymbols.get(nextPossibleSymbolIndex);
cellToFill.setSymbolIndex(nextSymbol);
addStep(cellToFill, steps);
if (this.solve(doku, steps)) {
return true;
}
cellToFill.setSymbolIndex(Cell.NOSYMBOL);
addStep(cellToFill, steps);
possibleSymbols.remove(nextPossibleSymbolIndex);
} }
return true; return false;
} }
} }

View File

@@ -3,9 +3,13 @@ package sudoku.solver;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
import java.util.logging.Level;
import gui.constants.Symbols;
import sudoku.io.SudokuPrinter;
import sudoku.structure.Cell; import sudoku.structure.Cell;
import sudoku.structure.MultiDoku; import sudoku.structure.MultiDoku;
import sudoku.structure.Sudoku;
public class RandomSolver implements Solver { public class RandomSolver implements Solver {
@@ -24,6 +28,13 @@ public class RandomSolver implements Solver {
if (Thread.interrupted()) if (Thread.interrupted())
throw new CancellationException("User wants to stop the solver"); throw new CancellationException("User wants to stop the solver");
Sudoku sudoku = doku.getSubGrid(0);
logger.log(Level.FINE,
'\n' + SudokuPrinter.toStringRectangleSudoku(sudoku,
sudoku.getBlockWidth() == 0 ? sudoku.getSize() : sudoku.getBlockWidth(),
sudoku.getBlockWidth() == 0 ? sudoku.getSize() : sudoku.getSize() / sudoku.getBlockWidth(),
Symbols.Numbers));
if (doku.isSolved()) { if (doku.isSolved()) {
return true; return true;
} }

View File

@@ -1,6 +1,5 @@
package sudoku.solver; package sudoku.solver;
import java.util.List;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
import sudoku.structure.MultiDoku; import sudoku.structure.MultiDoku;
@@ -9,9 +8,9 @@ import sudoku.structure.Sudoku;
/** /**
* Class de test non utilisé * Class de test non utilisé
*/ */
public class StupidSolver implements Solver{ public class StupidSolver {
private boolean solve(Sudoku sudoku, int index, List<SolverStep> steps) { private static boolean solve(Sudoku sudoku, int index) {
// mécanisme d'abandon // mécanisme d'abandon
if (Thread.interrupted()) if (Thread.interrupted())
throw new CancellationException("User wants to stop the solver"); throw new CancellationException("User wants to stop the solver");
@@ -22,30 +21,27 @@ public class StupidSolver implements Solver{
// si la case n'est pas modifiable, on passe à la suivante // si la case n'est pas modifiable, on passe à la suivante
if (!sudoku.getCell(index).isMutable()) if (!sudoku.getCell(index).isMutable())
return solve(sudoku, index + 1, steps); return solve(sudoku, index + 1);
for (int symbol = 0; symbol < sudoku.getSize(); symbol++) { for (int symbol = 0; symbol < sudoku.getSize(); symbol++) {
if (sudoku.getCell(index).trySetValue(symbol)) { if (sudoku.getCell(index).trySetValue(symbol)) {
addStep(sudoku.getCell(index), steps);
// on tente de placer sur la case suivante // on tente de placer sur la case suivante
if (solve(sudoku, index + 1, steps)) { if (solve(sudoku, index + 1)) {
return true; return true;
} }
} }
} }
// on a tout essayé et rien n'a fonctionné // on a tout essayé et rien n'a fonctionné
sudoku.getCell(index).empty(); sudoku.getCell(index).empty();
addStep(sudoku.getCell(index), steps);
return false; return false;
} }
@Override public static boolean solve(MultiDoku doku) {
public boolean solve(MultiDoku doku, List<SolverStep> steps) {
if (doku.isSolved()) if (doku.isSolved())
return true; return true;
for (Sudoku sudoku : doku.getSubGrids()) { for (Sudoku sudoku : doku.getSubGrids()) {
if (!solve(sudoku, 0, steps)) if (!solve(sudoku, 0))
return false; return false;
} }
return true; return true;

View File

@@ -234,24 +234,7 @@ 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));
} }
/** /**
@@ -287,6 +270,55 @@ public class SudokuFactory {
return new MultiDoku(Arrays.asList(sudoku1, sudoku2, sudoku3, sudoku4, sudoku5)); return new MultiDoku(Arrays.asList(sudoku1, sudoku2, sudoku3, sudoku4, sudoku5));
} }
/**
* TODO
* Créée un MultiDoku de Blocks carrés de taille size composé de cinq Sudokus,
* dont un central qui partage chacun de ses Blockss d'angle avec un autre
* Sudoku.
*
* @param size int, largeur des Blocks unitraires des Sudokus à crééer.
* @return MultiDoku, MultiDoku de forme X.
*/
public static MultiDoku createBasicPlusShapedMultidoku(int size, List<IConstraint> constraints) {
assert (size > 1);
return createBasicPlusShapedMultidoku(size, size, constraints);
}
/**
* TODO
* Créée un MultiDoku de Blocks rectangulaires de forme width par height composé
* de cinq Sudokus,
* dont un central qui partage chacun de ses Blocks d'angle avec un autre
* Sudoku.
*
* @param width int, largeur des Blocks unitraires des Sudokus à crééer.
* @param height int, hauteur des Blocks unitraires des Sudokus à crééer.
* @return MultiDoku, MultiDoku de forme X.
*/
public static MultiDoku createBasicPlusShapedMultidoku(int width, int height, List<IConstraint> constraints) {
assert (width > 1 && height > 1);
/*
* 3
* 2 1 4
* 5
*/
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));
}
public static void fillDoku(MultiDoku doku, Difficulty difficulty) throws Exception { public static void fillDoku(MultiDoku doku, Difficulty difficulty) throws Exception {
Solver solver = new RandomSolver(); Solver solver = new RandomSolver();
solver.solve(doku); solver.solve(doku);
@@ -309,8 +341,7 @@ public class SudokuFactory {
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++) {

View File

@@ -1,6 +1,7 @@
package sudoku; package sudoku;
import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.File; import java.io.File;
import java.util.Random; import java.util.Random;
@@ -21,7 +22,7 @@ public class SudokuSerializerTest {
new RandomSolver().solve(sudoku); new RandomSolver().solve(sudoku);
JSONObject data = SudokuSerializer.serializeSudoku(sudoku); JSONObject data = SudokuSerializer.serializeSudoku(sudoku);
MultiDoku multiDoku = SudokuSerializer.deserializeSudoku(data); MultiDoku multiDoku = SudokuSerializer.deserializeSudoku(data);
assertEquals(data.toString(), SudokuSerializer.serializeSudoku(multiDoku).toString()); assertTrue(data.toString().equals(SudokuSerializer.serializeSudoku(multiDoku).toString()));
} }
void testSaveWithSize(int blockWidth, int blockHeight) { void testSaveWithSize(int blockWidth, int blockHeight) {
@@ -40,7 +41,7 @@ public class SudokuSerializerTest {
File fileToDelete = new File(savePath); File fileToDelete = new File(savePath);
fileToDelete.delete(); fileToDelete.delete();
} catch (Exception e) { } catch (Exception e) {
System.out.println(e.getMessage()); e.printStackTrace();
assert false; assert false;
} }
} }
@@ -51,7 +52,7 @@ public class SudokuSerializerTest {
JSONObject data = SudokuSerializer.serializeSudoku(sudoku); JSONObject data = SudokuSerializer.serializeSudoku(sudoku);
MultiDoku multiDoku = SudokuSerializer.deserializeSudoku(data); MultiDoku multiDoku = SudokuSerializer.deserializeSudoku(data);
assertEquals(data.toString(), SudokuSerializer.serializeSudoku(multiDoku).toString()); assertTrue(data.toString().equals(SudokuSerializer.serializeSudoku(multiDoku).toString()));
} }
@Test @Test
@@ -59,17 +60,17 @@ public class SudokuSerializerTest {
Random r = new Random(); Random r = new Random();
int testCount = 20; int testCount = 20;
for (int i = 0; i < testCount; i++) { for (int i = 0; i < testCount; i++) {
int blockWidth = r.nextInt(10) + 1; int blockWidth = r.nextInt(4) + 1;
int blockHeight = r.nextInt(10) + 1; int blockHeight = r.nextInt(4) + 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(10) + 1; int blockWidth = r.nextInt(4) + 1;
int blockHeight = r.nextInt(10) + 1; int blockHeight = r.nextInt(4) + 1;
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(2) + 2;
testSerializeX(size); testSerializeX(size);
} }
} }

View File

@@ -4,62 +4,28 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
import java.util.List; import java.util.List;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import gui.constants.Symbols; import gui.constants.Symbols;
import sudoku.io.SudokuPrinter; import sudoku.io.SudokuPrinter;
import sudoku.io.SudokuSerializer; import sudoku.io.SudokuSerializer;
import sudoku.structure.*; import sudoku.structure.Cell;
import sudoku.structure.MultiDoku;
import sudoku.structure.Sudoku;
import sudoku.structure.SudokuFactory;
class SolverTest { class SolverTest {
private int ns = Cell.NOSYMBOL;
protected static HumanSolver h;
private static RandomSolver r;
private static MixedSolver m;
@BeforeAll @Test
public static void initializeSolvers(){ void solveTest() {
h = new HumanSolver(); MultiDoku dokuToTest = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
r = new RandomSolver(); MultiDoku dokuResult = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
m = new MixedSolver();
}
private void testSize2(Solver solver){ Sudoku sudokuToTest = dokuToTest.getSubGrid(0);
MultiDoku mdTest = SudokuFactory.createBasicEmptySquareDoku(2, SudokuFactory.DEFAULT_CONSTRAINTS); Sudoku sudokuResult = dokuResult.getSubGrid(0);
MultiDoku mdResult = SudokuFactory.createBasicEmptySquareDoku(2, SudokuFactory.DEFAULT_CONSTRAINTS);
Sudoku test = mdTest.getSubGrid(0);
Sudoku result = mdResult.getSubGrid(0);
List<Integer> immutableCells = List.of(
ns, 2, 3, ns,
0, ns, ns, ns,
ns, ns, ns, 3,
ns, 0, 1, ns);
assert (test.setImmutableCellsSymbol(immutableCells));
List<Integer> correctCells = List.of(
1, 2, 3, 0,
0, 3, 2, 1,
2, 1, 0, 3,
3, 0, 1, 2);
assert(result.setCellsSymbol(correctCells));
assert(result.isSolved());
solver.solve(mdTest); int ns = Cell.NOSYMBOL;
assert (mdTest.isSolved()); List<Integer> immutableCells = List.of(ns, ns, 0, ns, ns, 2, 8, ns, 1,
for (Cell cell : test.getCells()) {
cell.setImmutable();
}
assertEquals(SudokuSerializer.serializeSudoku(mdTest).toString(),
SudokuSerializer.serializeSudoku(mdResult).toString());
}
private void testSize3(Solver solver){
MultiDoku mdTest = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
MultiDoku mdResult = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
Sudoku test = mdTest.getSubGrid(0);
Sudoku result = mdResult.getSubGrid(0);
List<Integer> immutableCells = List.of(
ns, ns, 0, ns, ns, 2, 8, ns, 1,
ns, 3, ns, ns, 5, 6, 7, ns, ns, ns, 3, ns, ns, 5, 6, 7, ns, ns,
ns, ns, ns, 8, ns, 7, ns, ns, 6, ns, ns, ns, 8, ns, 7, ns, ns, 6,
0, ns, 1, ns, ns, ns, ns, ns, ns, 0, ns, 1, ns, ns, ns, ns, ns, ns,
@@ -68,9 +34,13 @@ class SolverTest {
ns, ns, 6, ns, ns, 8, ns, 7, 5, ns, ns, 6, ns, ns, 8, ns, 7, 5,
8, 0, ns, 7, ns, 5, 2, ns, 3, 8, 0, ns, 7, ns, 5, 2, ns, 3,
5, ns, ns, ns, 3, 1, 0, ns, ns); 5, ns, ns, ns, 3, 1, 0, ns, ns);
assert (test.setImmutableCellsSymbol(immutableCells));
List<Integer> correctCells = List.of( assert (sudokuToTest.setImmutableCellsSymbol(immutableCells));
7, 6, 0, 3, 4, 2, 8, 5, 1,
//SudokuPrinter.printRectangleSudoku(dokuToTest.getSubGrid(0), 3, 3);
SudokuPrinter.printMultiDoku(dokuToTest, 3, 3, Symbols.Numbers);
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,
0, 2, 1, 6, 8, 3, 5, 4, 7, 0, 2, 1, 6, 8, 3, 5, 4, 7,
@@ -79,46 +49,56 @@ 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.isSolved());
solver.solve(mdTest); sudokuResult.setCellsSymbol(correctCells);
assert (mdTest.isSolved());
for (Cell cell : test.getCells()) { System.out.println("\n****************************Doku Control\n");
SudokuPrinter.printRectangleSudoku(sudokuResult, 3, 3, Symbols.Russian);
assert (dokuResult.isSolved());
new RandomSolver().solve(dokuToTest);
System.out.println("\n****************************\nDoku solved");
//SudokuPrinter.printRectangleSudoku(dokuToTest.getSubGrid(0), 3, 3);
SudokuPrinter.printMultiDoku(dokuToTest, 3, 3, Symbols.Emojis);
assert (dokuToTest.isSolved());
for (Cell cell : sudokuToTest.getCells()) {
cell.setImmutable(); cell.setImmutable();
} }
assertEquals(SudokuSerializer.serializeSudoku(mdTest).toString(),
SudokuSerializer.serializeSudoku(mdResult).toString());
}
private void testMDSize3(Solver solver){ for (Cell cell : sudokuResult.getCells()) {
MultiDoku mdTest = SudokuFactory.createBasicXShapedMultidoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
try {
SudokuFactory.fillDoku(mdTest, Difficulty.Easy);
} catch (Exception e) {
assert(false);
}
MultiDoku result = SudokuSerializer.deserializeSudoku(SudokuSerializer.serializeSudoku(mdTest));
assert(result.isSolved());
solver.solve(mdTest);
assert (mdTest.isSolved());
for (Cell cell : mdTest.getCells()) {
cell.setImmutable(); cell.setImmutable();
} }
assertEquals(SudokuSerializer.serializeSudoku(mdTest).toString(),
SudokuSerializer.serializeSudoku(result).toString());
}
@Test assertEquals(SudokuSerializer.serializeSudoku(dokuResult).toString(),
void solveTest() { SudokuSerializer.serializeSudoku(dokuToTest).toString());
testSize2(h);
testSize3(h); MultiDoku dokuToTest2 = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
testMDSize3(h); Sudoku sudokuToTest2 = dokuToTest2.getSubGrid(0);
testSize2(m);
testSize3(m); List<Integer> immutableCells2 = List.of(ns, ns, 0, ns, ns, 2, 8, ns, 1,
testMDSize3(m); 1, 3, ns, ns, 5, 6, 7, ns, ns,
testSize2(r); ns, ns, ns, 8, ns, 7, ns, ns, 6,
testSize3(r); 0, ns, 1, ns, ns, ns, ns, ns, ns,
testMDSize3(r); 4, 8, 7, 5, 1, ns, 6, ns, ns,
6, ns, 3, 2, ns, ns, ns, 8, 0,
ns, ns, 6, ns, ns, 8, ns, 7, 5,
8, 0, ns, 7, ns, 5, 2, ns, 3,
5, ns, ns, ns, 3, 1, 0, ns, ns);
sudokuToTest2.setImmutableCellsSymbol(immutableCells2);
boolean isSolved = new RandomSolver().solve(dokuToTest2);
assert (!isSolved);
MultiDoku dokuToTest3 = SudokuFactory.createBasicEmptySquareDoku(3, SudokuFactory.DEFAULT_CONSTRAINTS);
new RandomSolver().solve(dokuToTest3);
//SudokuPrinter.printRectangleSudoku(dokuToTest3.getSubGrid(0), 3, 3);
SudokuPrinter.printMultiDoku(dokuToTest3, 3, 3, Symbols.Letters);
} }
} }