From fc7ae023877c4b784a49d96804466be1620e303b Mon Sep 17 00:00:00 2001 From: Melvyn Date: Thu, 23 Jan 2025 16:28:41 +0100 Subject: [PATCH 1/4] feat : Solver.solve() --- app/src/main/java/sudoku/Cell.java | 4 +++ app/src/main/java/sudoku/Main.java | 8 ------ app/src/main/java/sudoku/MultiDoku.java | 19 +++++++++++++ app/src/main/java/sudoku/Sudoku.java | 27 ++++++++++++++++++ app/src/main/java/sudoku/solver/Solver.java | 31 ++++++++++++++++++++- 5 files changed, 80 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/sudoku/Cell.java b/app/src/main/java/sudoku/Cell.java index 32be9b7..6cb1847 100644 --- a/app/src/main/java/sudoku/Cell.java +++ b/app/src/main/java/sudoku/Cell.java @@ -72,6 +72,10 @@ public abstract class Cell { } */ + public boolean isEmpty() { + return this.symbolIndex == Cell.NOSYMBOL; + } + public boolean equalsValue(Cell otherCell) { return otherCell.getSymbolIndex() == this.getSymbolIndex(); } diff --git a/app/src/main/java/sudoku/Main.java b/app/src/main/java/sudoku/Main.java index fe57633..2b32777 100644 --- a/app/src/main/java/sudoku/Main.java +++ b/app/src/main/java/sudoku/Main.java @@ -3,17 +3,9 @@ */ package sudoku; -import sudoku.constraint.BlockConstraint; -import sudoku.constraint.ColumnConstraint; -import sudoku.constraint.IConstraint; -import sudoku.constraint.LineConstraint; import sudoku.io.SudokuPrinter; -import sudoku.io.SudokuSerializer; -import sudoku.solver.Solver; -import java.util.ArrayList; import java.util.Arrays; -import java.util.List; public class Main { public String getGreeting() { diff --git a/app/src/main/java/sudoku/MultiDoku.java b/app/src/main/java/sudoku/MultiDoku.java index 0e4a2c5..59a170d 100644 --- a/app/src/main/java/sudoku/MultiDoku.java +++ b/app/src/main/java/sudoku/MultiDoku.java @@ -50,4 +50,23 @@ public class MultiDoku { sb.append("\n}"); return sb.toString(); } + + public MutableCell getFirstEmptyMutableCell() { + for (Sudoku sudoku : this.subGrids) { + MutableCell cellTmp = sudoku.getFirstEmptyMutableCell(); + if (cellTmp.isEmpty()) { + return cellTmp; + } + } + return null; + } + + public List getPossibleSymbolsOfCell(MutableCell cellToFill) { + for (Sudoku sudoku : this.subGrids) { + if (sudoku.contains(cellToFill)) { + return sudoku.getPossibleSymbolsOfCell(cellToFill); + } + } + return new ArrayList<>(); + } } diff --git a/app/src/main/java/sudoku/Sudoku.java b/app/src/main/java/sudoku/Sudoku.java index e0401e4..0ee1776 100644 --- a/app/src/main/java/sudoku/Sudoku.java +++ b/app/src/main/java/sudoku/Sudoku.java @@ -162,4 +162,31 @@ public class Sudoku { sb.append("\n}"); return sb.toString(); } + + public MutableCell getFirstEmptyMutableCell() { + for (MutableCell cell : this.getMutableCells()) { + if (cell.isEmpty()) { + return cell; + } + } + return null; + } + + public List getPossibleSymbolsOfCell(MutableCell cellToFill) { + List result = new ArrayList<>(); + Coordinate cellCoordinates; + try { + cellCoordinates = this.getCoordinateCell(cellToFill); + } catch (Exception e) { + return result; + } + for (IConstraint constraint : this.constraints) { + if (result.isEmpty()) { + result.addAll(constraint.getPossibleSymbols(this, cellCoordinates.getX(), cellCoordinates.getY())); + } else { + result.retainAll(constraint.getPossibleSymbols(this, cellCoordinates.getX(), cellCoordinates.getY())); + } + } + return result; + } } diff --git a/app/src/main/java/sudoku/solver/Solver.java b/app/src/main/java/sudoku/solver/Solver.java index 59da5eb..87aeac8 100644 --- a/app/src/main/java/sudoku/solver/Solver.java +++ b/app/src/main/java/sudoku/solver/Solver.java @@ -10,14 +10,41 @@ import java.util.Random; import java.util.Stack; public class Solver { - Stack stack; public Solver() {} + /** + * Résout le multidoku passé en paramètre si c'est possible. + * En testant toutes les possibilités avec un algorithme de backtracking. + * @param doku Multidouke, à résoudre + * @return boolean, true s'il est résolut ou false s'il ne l'est pas. + */ + public boolean solve(MultiDoku doku) { + MutableCell cellToFill = doku.getFirstEmptyMutableCell(); + if (cellToFill == null) { + return true; + } + + List possibleSymbols = doku.getPossibleSymbolsOfCell(cellToFill); + if (possibleSymbols.isEmpty()) { + return false; + } + + for (int symbol : possibleSymbols) { + cellToFill.setSymbolIndex(symbol); + return this.solve(doku); + } + return false; + } + + /* + Ancien algo abandonné pour le moment + private void rollBack() { stack.pop(); } + public MultiDoku solve(MultiDoku doku, List constraints) throws Exception { List allMutableCells = doku.getMutableCells(); List remainingCellsToCheck = new ArrayList<>(allMutableCells); @@ -39,4 +66,6 @@ public class Solver { } return doku; } + + */ } From 690232110195ca30bcd28210dd8af052f2f5a176 Mon Sep 17 00:00:00 2001 From: Melvyn Date: Thu, 23 Jan 2025 16:29:54 +0100 Subject: [PATCH 2/4] =?UTF-8?q?fix=20:=20passage=20de=20solve()=20en=20m?= =?UTF-8?q?=C3=A9tode=20static?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/src/main/java/sudoku/solver/Solver.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/sudoku/solver/Solver.java b/app/src/main/java/sudoku/solver/Solver.java index 87aeac8..6c933ad 100644 --- a/app/src/main/java/sudoku/solver/Solver.java +++ b/app/src/main/java/sudoku/solver/Solver.java @@ -11,15 +11,13 @@ import java.util.Stack; public class Solver { - public Solver() {} - /** * Résout le multidoku passé en paramètre si c'est possible. * En testant toutes les possibilités avec un algorithme de backtracking. * @param doku Multidouke, à résoudre * @return boolean, true s'il est résolut ou false s'il ne l'est pas. */ - public boolean solve(MultiDoku doku) { + public static boolean solve(MultiDoku doku) { MutableCell cellToFill = doku.getFirstEmptyMutableCell(); if (cellToFill == null) { return true; @@ -32,7 +30,7 @@ public class Solver { for (int symbol : possibleSymbols) { cellToFill.setSymbolIndex(symbol); - return this.solve(doku); + return Solver.solve(doku); } return false; } From 483b286bac1b4674d48a38b678015121746e15b5 Mon Sep 17 00:00:00 2001 From: Melvyn Date: Thu, 23 Jan 2025 23:20:16 +0100 Subject: [PATCH 3/4] feat : solver + test --- app/src/main/java/sudoku/Block.java | 8 ++ app/src/main/java/sudoku/Cell.java | 110 +++++++---------- app/src/main/java/sudoku/Coordinate.java | 6 +- app/src/main/java/sudoku/MultiDoku.java | 50 ++++++-- app/src/main/java/sudoku/MutableCell.java | 47 -------- app/src/main/java/sudoku/Sudoku.java | 113 +++++++++++++----- app/src/main/java/sudoku/SudokuFactory.java | 14 ++- .../sudoku/constraint/BlockConstraint.java | 5 +- .../sudoku/constraint/ColumnConstraint.java | 2 +- .../sudoku/constraint/LineConstraint.java | 2 +- .../main/java/sudoku/io/SudokuSerializer.java | 30 +++-- app/src/main/java/sudoku/solver/Solver.java | 21 ++-- .../test/java/sudoku/solver/SolverTest.java | 67 +++++++++++ 13 files changed, 292 insertions(+), 183 deletions(-) delete mode 100644 app/src/main/java/sudoku/MutableCell.java create mode 100644 app/src/test/java/sudoku/solver/SolverTest.java diff --git a/app/src/main/java/sudoku/Block.java b/app/src/main/java/sudoku/Block.java index df287e4..d7ededc 100644 --- a/app/src/main/java/sudoku/Block.java +++ b/app/src/main/java/sudoku/Block.java @@ -31,4 +31,12 @@ public class Block { return false; } + public boolean containsCell(Cell cell) { + for (Cell cellTmp : this.cells) { + if (cellTmp.equals(cell)) { + return true; + } + } + return false; + } } diff --git a/app/src/main/java/sudoku/Cell.java b/app/src/main/java/sudoku/Cell.java index 6cb1847..d66013e 100644 --- a/app/src/main/java/sudoku/Cell.java +++ b/app/src/main/java/sudoku/Cell.java @@ -1,98 +1,74 @@ package sudoku; -import org.checkerframework.checker.units.qual.C; - import java.util.ArrayList; import java.util.List; -public abstract class Cell { +public class Cell { - protected static int NOSYMBOL = -1; - protected int symbolIndex; - protected Block block = null; - //protected List blockContainers = null; - //protected List sudokuContainers = null; + public static int NOSYMBOL = -1; + + private Block blockContainer; + private int symbolIndex = Cell.NOSYMBOL; + private final List possibleSymbols; + private boolean isMutable = true; + + public Cell() { + this.possibleSymbols = new ArrayList<>(); + } public Cell(int symbolIndex) { this.symbolIndex = symbolIndex; + this.possibleSymbols = new ArrayList<>(); } - /** - * @brief Default constructor, empty square - */ - public Cell() { - this(NOSYMBOL); - } - - /* - - public Cell(Cell cell) { - this.symbolIndex = cell.symbolIndex; - this.sudokuContainers = cell.sudokuContainers; - this.blockContainers = cell.blockContainers; - } - - public Cell(List sudokuContainers, List blockContainers) { - super(); - this.sudokuContainers = new ArrayList<>(sudokuContainers); - this.blockContainers = new ArrayList<>(blockContainers); - } - - */ - public int getSymbolIndex() { - return symbolIndex; + return this.symbolIndex; + } + + public void setSymbolIndex(int symbolIndex) { + this.symbolIndex = symbolIndex; + } + + public void setPossibleSymbols(List possibleSymbols) { + this.possibleSymbols.clear(); + this.possibleSymbols.addAll(possibleSymbols); + } + + public void setImmutable() { + this.isMutable = false; } public Block getBlock() { - return this.block; + return this.blockContainer; } - /* - public List getBlockContainers() { - return this.blockContainers; - } - - public void setBlockContainers(List block) { - this.blockContainers = block; - } - - public List getSudokuContainers() { - return this.sudokuContainers; - } -*/ - // only SudokuFactory and SudokuSerializer should access this public void setBlock(Block block) { - this.block = block; + this.blockContainer = block; } - /* - public void setSudokuContainers(List sudoku) { - this.sudokuContainers = sudoku; + /** + * Remove the current symbolIndex and returns it + * @return integer symbolIndex cleared + */ + public int clearCurrentSymbol() { + int i = this.symbolIndex; + setSymbolIndex(NOSYMBOL); + return i; } - */ public boolean isEmpty() { return this.symbolIndex == Cell.NOSYMBOL; } - public boolean equalsValue(Cell otherCell) { - return otherCell.getSymbolIndex() == this.getSymbolIndex(); + public void removeSymbolFromPossibilities(int indexSymbol) { + possibleSymbols.remove(indexSymbol); } - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - sb.append("| "); - if (this.symbolIndex != NOSYMBOL){ - sb.append(this.symbolIndex); - } - else { - sb.append(" "); - } - - sb.append(" |"); - return sb.toString(); + public List getPossibleSymbols() { + return this.possibleSymbols; } + public boolean isMutable() { + return this.isMutable; + } } diff --git a/app/src/main/java/sudoku/Coordinate.java b/app/src/main/java/sudoku/Coordinate.java index 668dd85..c00b8ad 100644 --- a/app/src/main/java/sudoku/Coordinate.java +++ b/app/src/main/java/sudoku/Coordinate.java @@ -5,9 +5,9 @@ public class Coordinate { private int x; private int y; - public Coordinate(int row, int col) { - this.x = row; - this.y = col; + public Coordinate(int x, int y) { + this.x = x; + this.y = y; } public int getX() { diff --git a/app/src/main/java/sudoku/MultiDoku.java b/app/src/main/java/sudoku/MultiDoku.java index 59a170d..1c65754 100644 --- a/app/src/main/java/sudoku/MultiDoku.java +++ b/app/src/main/java/sudoku/MultiDoku.java @@ -1,7 +1,5 @@ package sudoku; -import sudoku.constraint.IConstraint; - import java.util.ArrayList; import java.util.List; @@ -26,12 +24,12 @@ public class MultiDoku { return subGrids.get(i); } - public List getMutableCells(){ - List mutableCells = new ArrayList<>(); + public List getCells(){ + List cells = new ArrayList<>(); for (Sudoku sudoku : subGrids){ - mutableCells.addAll(sudoku.getMutableCells()); + cells.addAll(sudoku.getCells()); } - return mutableCells; + return cells; } public void updateSymbolsPossibilities() throws Exception { @@ -51,17 +49,17 @@ public class MultiDoku { return sb.toString(); } - public MutableCell getFirstEmptyMutableCell() { + public Cell getFirstEmptyCell() { for (Sudoku sudoku : this.subGrids) { - MutableCell cellTmp = sudoku.getFirstEmptyMutableCell(); - if (cellTmp.isEmpty()) { + Cell cellTmp = sudoku.getFirstEmptyCell(); + if (cellTmp != null && cellTmp.isEmpty()) { return cellTmp; } } return null; } - public List getPossibleSymbolsOfCell(MutableCell cellToFill) { + public List getPossibleSymbolsOfCell(Cell cellToFill) { for (Sudoku sudoku : this.subGrids) { if (sudoku.contains(cellToFill)) { return sudoku.getPossibleSymbolsOfCell(cellToFill); @@ -69,4 +67,36 @@ public class MultiDoku { } return new ArrayList<>(); } + + public List getSubGrids() { + return this.subGrids; + } + + public boolean isValid() { + boolean result = true; + for (Sudoku sudoku : this.subGrids) { + result = sudoku.isValid() && result; + } + return result; + } + + @Override + public boolean equals(Object object) { + if (!(object instanceof MultiDoku)) { + return false; + } + + if (this.getNbSubGrids() != ((MultiDoku) object).getNbSubGrids()) { + return false; + } + + for (int i = 0; i < this.getNbSubGrids(); i++) { + if (!this.getSubGrid(i).equals(((MultiDoku) object).getSubGrid(i))) { + return false; + } + } + + return true; + } } + diff --git a/app/src/main/java/sudoku/MutableCell.java b/app/src/main/java/sudoku/MutableCell.java deleted file mode 100644 index 9673b8a..0000000 --- a/app/src/main/java/sudoku/MutableCell.java +++ /dev/null @@ -1,47 +0,0 @@ -package sudoku; - -import java.util.ArrayList; -import java.util.List; - -public class MutableCell extends Cell{ - - private final List possibleSymbols; - - public MutableCell() { - super(); - this.possibleSymbols = new ArrayList<>(); - } - - public MutableCell(int symbolIndex) { - super(symbolIndex); - this.possibleSymbols = new ArrayList<>(); - } - - public void setSymbolIndex(int symbolIndex) { - this.symbolIndex = symbolIndex; - } - - public void setPossibleSymbols(List possibleSymbols) { - this.possibleSymbols.clear(); - this.possibleSymbols.addAll(possibleSymbols); - } - - /** - * Remove the current symbolIndex and returns it - * @return integer symbolIndex cleared - */ - public int clearCurrentSymbol() { - int i = this.symbolIndex; - setSymbolIndex(NOSYMBOL); - return i; - } - - public void removeSymbolFromPossibilities(int indexSymbol) { - possibleSymbols.remove(indexSymbol); - } - - public List getPossibleSymbols() { - return this.possibleSymbols; - } - -} diff --git a/app/src/main/java/sudoku/Sudoku.java b/app/src/main/java/sudoku/Sudoku.java index 0ee1776..ccd4f75 100644 --- a/app/src/main/java/sudoku/Sudoku.java +++ b/app/src/main/java/sudoku/Sudoku.java @@ -14,6 +14,7 @@ public class Sudoku { private final List blocks; private final List cells; private final List constraints; + private boolean isMutable; public Sudoku(List cells, List blocks, List constraints) { this.cells = cells; @@ -32,24 +33,36 @@ public class Sudoku { /** * Try to place a cell at the given coordinate * - * @return false if it can't be done + * @return Cell created or null if it can't be done */ - public boolean setCellSymbol(int x, int y, int value) { + public Cell setCellSymbol(int x, int y, int value) { assert (isValidCoords(x, y)); + /* for (IConstraint constraint : this.constraints) { if (!constraint.canBePlaced(this, x, y, value)) { - return false; + return null; } } + */ Cell cell = getCell(x, y); - if (cell instanceof MutableCell mCell) { - mCell.setSymbolIndex(value); - return true; - } - return false; + cell.setSymbolIndex(value); + return cell; } public boolean setCellsSymbol(List values) { + if (values.size() > this.cells.size()) { + return false; + } + for (int i = 0; i < values.size(); i++) { + int x = i % this.blocks.size(); + int y = (i-x) / this.blocks.size(); + int value = values.get(i); + this.setCellSymbol(x, y, value); + } + return true; + } + + public boolean setImmutableCellsSymbol(List values) { if (values.size() > this.cells.size()) { return false; } @@ -57,18 +70,27 @@ public class Sudoku { int x = i%this.blocks.size(); int y = (i-x)/this.blocks.size(); int value = values.get(i); - if (!this.setCellSymbol(x, y, value)) { - return false; + if (value != Cell.NOSYMBOL) { + Cell cellPlaced = this.setCellSymbol(x, y, value); + if (cellPlaced == null) { + continue; + } + cellPlaced.setImmutable(); } + } return true; - } + } public Cell getCell(int x, int y) { int index = y * getSize() + x; assert (isValidCoords(x, y)); - return this.cells.get(index); + try { + return this.cells.get(index); + } catch (IndexOutOfBoundsException e) { + return null; + } } public Cell getCell(int i) { @@ -91,16 +113,6 @@ public class Sudoku { return this.blocks; } - public List getMutableCells() { - List mutableCells = new ArrayList<>(); - for (Cell cell : this.cells) { - if (cell instanceof MutableCell) { - mutableCells.add((MutableCell) cell); - } - } - return mutableCells; - } - public boolean contains(Cell cell) { return this.cells.contains(cell); } @@ -129,8 +141,8 @@ public class Sudoku { public void updateSymbolsPossibilities() throws Exception { for (IConstraint constraint : constraints) { - List mutableCells = this.getMutableCells(); - for (MutableCell cell : mutableCells) { + List cells = this.getCells(); + for (Cell cell : cells) { Coordinate coord = null; try { coord = this.getCoordinateCell(cell); @@ -163,8 +175,8 @@ public class Sudoku { return sb.toString(); } - public MutableCell getFirstEmptyMutableCell() { - for (MutableCell cell : this.getMutableCells()) { + public Cell getFirstEmptyCell() { + for (Cell cell : this.cells) { if (cell.isEmpty()) { return cell; } @@ -172,7 +184,7 @@ public class Sudoku { return null; } - public List getPossibleSymbolsOfCell(MutableCell cellToFill) { + public List getPossibleSymbolsOfCell(Cell cellToFill) { List result = new ArrayList<>(); Coordinate cellCoordinates; try { @@ -189,4 +201,51 @@ public class Sudoku { } return result; } + + public boolean isValid() { + for (Cell cell : this.cells) { + if (cell.isMutable()) { + if (cell.isEmpty()) { + return false; + } + for (IConstraint constraint : this.constraints) { + Coordinate coords; + try { + int symbolPlaced = cell.getSymbolIndex(); + coords = this.getCoordinateCell(cell); + + cell.setSymbolIndex(Cell.NOSYMBOL); + List possibleSymbols = constraint.getPossibleSymbols(this, coords.getX(), coords.getY()); + cell.setSymbolIndex(symbolPlaced); + if (possibleSymbols.size() != 1 || possibleSymbols.get(0) != symbolPlaced) { + return false; + } + + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + } + return true; + } + + @Override + public boolean equals(Object object) { + if (!(object instanceof Sudoku)) { + return false; + } + + if (this.getSize() != ((Sudoku) object).getSize()) { + return false; + } + + for (int i = 0; i < this.getSize(); i++) { + if (this.getCell(i).getSymbolIndex() != ((Sudoku) object).getCell(i).getSymbolIndex()) { + return false; + } + } + + return true; + } } diff --git a/app/src/main/java/sudoku/SudokuFactory.java b/app/src/main/java/sudoku/SudokuFactory.java index 61c4887..87f5c30 100644 --- a/app/src/main/java/sudoku/SudokuFactory.java +++ b/app/src/main/java/sudoku/SudokuFactory.java @@ -7,13 +7,14 @@ import sudoku.constraint.LineConstraint; import java.util.ArrayList; import java.util.List; +import java.util.Map; public class SudokuFactory { private static List initCells(int size) { List cells = new ArrayList<>(size * size); for (int i = 0; i < size * size; i++) { - cells.add(new MutableCell()); + cells.add(new Cell()); } return cells; } @@ -59,4 +60,15 @@ public class SudokuFactory { return createBasicEmptyRectangleSudoku(size, size); } + public static void setIMMutableCells(MultiDoku doku, Map immutableCells) { + immutableCells.forEach((coordinate, symbol) -> { + for (Sudoku sudoku : doku.getSubGrids()) { + Cell cell = sudoku.getCell(coordinate.getX(), coordinate.getY()); + if (cell != null) { + cell.setSymbolIndex(symbol); + cell.setImmutable(); + } + } + }); + } } diff --git a/app/src/main/java/sudoku/constraint/BlockConstraint.java b/app/src/main/java/sudoku/constraint/BlockConstraint.java index 943fc97..caa1667 100644 --- a/app/src/main/java/sudoku/constraint/BlockConstraint.java +++ b/app/src/main/java/sudoku/constraint/BlockConstraint.java @@ -1,14 +1,15 @@ package sudoku.constraint; import sudoku.Block; +import sudoku.Cell; import sudoku.Sudoku; public class BlockConstraint implements IConstraint{ @Override public boolean canBePlaced(final Sudoku s, int x, int y, int newSymbolIndex) { - Block bloc = s.getCell(x, y).getBlock(); - return !bloc.containsSymbol(newSymbolIndex); + Block block = s.getCell(x, y).getBlock(); + return !block.containsSymbol(newSymbolIndex); } } diff --git a/app/src/main/java/sudoku/constraint/ColumnConstraint.java b/app/src/main/java/sudoku/constraint/ColumnConstraint.java index 881faab..7045b00 100644 --- a/app/src/main/java/sudoku/constraint/ColumnConstraint.java +++ b/app/src/main/java/sudoku/constraint/ColumnConstraint.java @@ -7,7 +7,7 @@ public class ColumnConstraint implements IConstraint { @Override public boolean canBePlaced(final Sudoku s, int x, int y, int newSymbolIndex) { for (int i = 0; i < s.getSize(); i++) { - if (s.getCell(x, newSymbolIndex).getSymbolIndex() == newSymbolIndex) + if (s.getCell(x, i).getSymbolIndex() == newSymbolIndex) return false; } return true; diff --git a/app/src/main/java/sudoku/constraint/LineConstraint.java b/app/src/main/java/sudoku/constraint/LineConstraint.java index 2f9a81e..b568928 100644 --- a/app/src/main/java/sudoku/constraint/LineConstraint.java +++ b/app/src/main/java/sudoku/constraint/LineConstraint.java @@ -7,7 +7,7 @@ public class LineConstraint implements IConstraint { @Override public boolean canBePlaced(final Sudoku s, int x, int y, int newSymbolIndex) { for (int i = 0; i < s.getSize(); i++) { - if (s.getCell(newSymbolIndex, y).getSymbolIndex() == newSymbolIndex) + if (s.getCell(i, y).getSymbolIndex() == newSymbolIndex) return false; } return true; diff --git a/app/src/main/java/sudoku/io/SudokuSerializer.java b/app/src/main/java/sudoku/io/SudokuSerializer.java index b0ab311..0fdd96a 100644 --- a/app/src/main/java/sudoku/io/SudokuSerializer.java +++ b/app/src/main/java/sudoku/io/SudokuSerializer.java @@ -10,17 +10,16 @@ import sudoku.Block; import sudoku.Cell; import sudoku.ImmutableCell; import sudoku.MultiDoku; -import sudoku.MutableCell; import sudoku.Sudoku; public class SudokuSerializer { public static String serializeSudoku(final MultiDoku multidoku) { - List cellIds = new ArrayList<>(); + List cellIds = new ArrayList<>(); List blockIds = new ArrayList<>(); - JSONObject jsonRoot = new JSONObject(); - JSONArray jsonCells = new JSONArray(); + JSONObject jsonRoot = new JSONObject(); + JSONArray jsonCells = new JSONArray(); JSONArray jsonBlocks = new JSONArray(); JSONArray jsonSudokus = new JSONArray(multidoku.getNbSubGrids()); @@ -55,17 +54,16 @@ public class SudokuSerializer { // init blocks - for (int i = 0; i < blockIds.size(); i++) { - JSONObject blockJsonObject = new JSONObject(); - JSONArray cellsJsonArray = new JSONArray(); - Block block = blockIds.get(i); - for (Cell cell : block.getCells()) { - int cellID = cellIds.indexOf(cell); - cellsJsonArray.put(cellID); - } - blockJsonObject.put("cellIDs", cellsJsonArray); - jsonBlocks.put(blockJsonObject); - } + for (Block blockId : blockIds) { + JSONObject blockJsonObject = new JSONObject(); + JSONArray cellsJsonArray = new JSONArray(); + for (Cell cell : blockId.getCells()) { + int cellID = cellIds.indexOf(cell); + cellsJsonArray.put(cellID); + } + blockJsonObject.put("cellIDs", cellsJsonArray); + jsonBlocks.put(blockJsonObject); + } for (int i = 0; i < multidoku.getNbSubGrids(); i++) { // serialise sub grid @@ -115,7 +113,7 @@ public class SudokuSerializer { if (entry.has("immutable")) { cells.add(new ImmutableCell(symboleIndex)); } else { - cells.add(new MutableCell(symboleIndex)); + cells.add(new Cell(symboleIndex)); } } diff --git a/app/src/main/java/sudoku/solver/Solver.java b/app/src/main/java/sudoku/solver/Solver.java index 6c933ad..c541309 100644 --- a/app/src/main/java/sudoku/solver/Solver.java +++ b/app/src/main/java/sudoku/solver/Solver.java @@ -1,13 +1,10 @@ package sudoku.solver; import sudoku.MultiDoku; -import sudoku.MutableCell; -import sudoku.constraint.IConstraint; +import sudoku.Cell; +import sudoku.io.SudokuPrinter; -import java.util.ArrayList; import java.util.List; -import java.util.Random; -import java.util.Stack; public class Solver { @@ -18,11 +15,15 @@ public class Solver { * @return boolean, true s'il est résolut ou false s'il ne l'est pas. */ public static boolean solve(MultiDoku doku) { - MutableCell cellToFill = doku.getFirstEmptyMutableCell(); - if (cellToFill == null) { + if (doku.isValid()) { return true; } + Cell cellToFill = doku.getFirstEmptyCell(); + if (cellToFill == null) { + return false; + } + List possibleSymbols = doku.getPossibleSymbolsOfCell(cellToFill); if (possibleSymbols.isEmpty()) { return false; @@ -30,7 +31,11 @@ public class Solver { for (int symbol : possibleSymbols) { cellToFill.setSymbolIndex(symbol); - return Solver.solve(doku); + if (Solver.solve(doku)) { + return true; + } else { + cellToFill.setSymbolIndex(Cell.NOSYMBOL); + } } return false; } diff --git a/app/src/test/java/sudoku/solver/SolverTest.java b/app/src/test/java/sudoku/solver/SolverTest.java new file mode 100644 index 0000000..cd974a4 --- /dev/null +++ b/app/src/test/java/sudoku/solver/SolverTest.java @@ -0,0 +1,67 @@ +package sudoku.solver; + +import org.junit.jupiter.api.Test; +import sudoku.*; +import sudoku.io.SudokuPrinter; + +import java.util.List; + +class SolverTest { + + @Test + void solveTest() { + + MultiDoku dokuToTest = SudokuFactory.createBasicEmptySquareSudoku(3); + MultiDoku dokuResult = SudokuFactory.createBasicEmptySquareSudoku(3); + + Sudoku sudokuToTest = dokuToTest.getSubGrid(0); + Sudoku sudokuResult = dokuResult.getSubGrid(0); + + int ns = Cell.NOSYMBOL; + List immutableCells = List.of(ns, ns, 0, ns, ns, 2, 8, ns, 1, + ns, 3, ns, ns, 5, 6, 7, ns, ns, + ns, ns, ns, 8, ns, 7, ns, ns, 6, + 0, ns, 1, ns, ns, ns, ns, ns, ns, + 4, 8, 7, 5, 1, ns, 6, ns, ns, + 6, ns, 3, 2, ns, ns, ns, 8, 0, + ns, ns, 6, ns, ns, 8, ns, 7, 5, + 8, 0, ns, 7, ns, 5, 2, ns, 3, + 5, ns, ns, ns, 3, 1, 0, ns, ns); + + assert(sudokuToTest.setImmutableCellsSymbol(immutableCells)); + + + SudokuPrinter.printRectangleSudoku(dokuToTest.getSubGrid(0), 3, 3); + + + List correctCells = List.of(7, 6, 0, 3, 4, 2, 8, 5, 1, + 2, 3, 8, 1, 5, 6, 7, 0, 4, + 1, 4, 5, 8, 0, 7, 3, 2, 6, + 0, 2, 1, 6, 8, 3, 5, 4, 7, + 4, 8, 7, 5, 1, 0, 6, 3, 2, + 6, 5, 3, 2, 7, 4, 1, 8, 0, + 3, 1, 6, 0, 2, 8, 4, 7, 5, + 8, 0, 4, 7, 6, 5, 2, 1, 3, + 5, 7, 2, 4, 3, 1, 0, 6, 8); + + sudokuResult.setCellsSymbol(correctCells); + + + System.out.println("\n****************************Doku Control\n"); + SudokuPrinter.printRectangleSudoku(sudokuResult, 3, 3); + + + assert(dokuResult.isValid()); + + boolean isSolvable = Solver.solve(dokuToTest); + + + System.out.println("\n****************************\nDoku solve"); + SudokuPrinter.printRectangleSudoku(dokuToTest.getSubGrid(0), 3, 3); + + + assert(dokuToTest.isValid()); + + assert(dokuToTest.equals(dokuResult)); + } +} \ No newline at end of file From 187bcea939bf09aac47a1f7bdd2a00054685f969 Mon Sep 17 00:00:00 2001 From: Melvyn Date: Thu, 23 Jan 2025 23:47:12 +0100 Subject: [PATCH 4/4] fix : solverTest --- app/src/main/java/sudoku/solver/Solver.java | 2 +- .../test/java/sudoku/solver/SolverTest.java | 22 +++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/sudoku/solver/Solver.java b/app/src/main/java/sudoku/solver/Solver.java index c541309..e075461 100644 --- a/app/src/main/java/sudoku/solver/Solver.java +++ b/app/src/main/java/sudoku/solver/Solver.java @@ -11,7 +11,7 @@ public class Solver { /** * Résout le multidoku passé en paramètre si c'est possible. * En testant toutes les possibilités avec un algorithme de backtracking. - * @param doku Multidouke, à résoudre + * @param doku Multidoku, à résoudre * @return boolean, true s'il est résolut ou false s'il ne l'est pas. */ public static boolean solve(MultiDoku doku) { diff --git a/app/src/test/java/sudoku/solver/SolverTest.java b/app/src/test/java/sudoku/solver/SolverTest.java index cd974a4..0617e33 100644 --- a/app/src/test/java/sudoku/solver/SolverTest.java +++ b/app/src/test/java/sudoku/solver/SolverTest.java @@ -53,15 +53,33 @@ class SolverTest { assert(dokuResult.isValid()); - boolean isSolvable = Solver.solve(dokuToTest); + Solver.solve(dokuToTest); - System.out.println("\n****************************\nDoku solve"); + System.out.println("\n****************************\nDoku solved"); SudokuPrinter.printRectangleSudoku(dokuToTest.getSubGrid(0), 3, 3); assert(dokuToTest.isValid()); assert(dokuToTest.equals(dokuResult)); + + MultiDoku dokuToTest2 = SudokuFactory.createBasicEmptySquareSudoku(3); + Sudoku sudokuToTest2 = dokuToTest2.getSubGrid(0); + + List immutableCells2 = List.of(ns, ns, 0, ns, ns, 2, 8, ns, 1, + 1, 3, ns, ns, 5, 6, 7, ns, ns, + ns, ns, ns, 8, ns, 7, ns, ns, 6, + 0, ns, 1, ns, ns, ns, ns, ns, ns, + 4, 8, 7, 5, 1, ns, 6, ns, ns, + 6, ns, 3, 2, ns, ns, ns, 8, 0, + ns, ns, 6, ns, ns, 8, ns, 7, 5, + 8, 0, ns, 7, ns, 5, 2, ns, 3, + 5, ns, ns, ns, 3, 1, 0, ns, ns); + sudokuToTest2.setImmutableCellsSymbol(immutableCells2); + + boolean isSolved = Solver.solve(dokuToTest2); + + assert(!isSolved); } } \ No newline at end of file