diff --git a/app/src/main/java/sudoku/io/ConsoleInterface.java b/app/src/main/java/sudoku/io/ConsoleInterface.java index cc42396..1944b88 100644 --- a/app/src/main/java/sudoku/io/ConsoleInterface.java +++ b/app/src/main/java/sudoku/io/ConsoleInterface.java @@ -3,10 +3,8 @@ package sudoku.io; import gui.RenderableMultidoku; import gui.Symbols; import sudoku.constraint.*; -import sudoku.solver.RandomSolver; -import sudoku.structure.Difficulty; -import sudoku.structure.MultiDoku; -import sudoku.structure.SudokuFactory; +import sudoku.solver.*; +import sudoku.structure.*; import java.util.ArrayList; import java.util.List; @@ -15,14 +13,13 @@ import java.util.Scanner; public class ConsoleInterface { public Scanner reader = new Scanner(System.in); - public void welcome(){ + public void welcome() { System.out.println("Welcome to our Sudoku Solver!"); System.out.println("This is the project of Melvyn Bauvent, Lilas Grenier and Simon Priblyski."); System.out.println("Do you have a save sudoku you would like to continue? (y/n, default n)"); - if (reader.next().equalsIgnoreCase("y")){ + if (reader.next().equalsIgnoreCase("y")) { useSavedDoku(); - } - else { + } else { createDoku(); } } @@ -32,12 +29,16 @@ public class ConsoleInterface { MultiDoku md = saveChoice(); int blockWidth = md.getSubGrid(0).getBlockWidth(); int blockHeight = md.getSubGrid(0).getBlocks().getFirst().getCells().size() / blockWidth; - List listSymbols = pickSymbols(blockWidth*blockHeight); + List listSymbols = pickSymbols(blockWidth * blockHeight); System.out.println("This is the saved sudoku:"); showMultidoku(md, listSymbols, blockWidth, blockHeight); + do { + turn(md, listSymbols, blockWidth, blockHeight); + } while (!md.isSolved()); + congrats(); } - public void createDoku(){ + public void createDoku() { System.out.println("First of all, you need to tell me the size of the sudoku you want to generate."); int width = getBlockWidth(); int height = getBlockHeight(); @@ -51,26 +52,53 @@ public class ConsoleInterface { MultiDoku doku; if (reader.next().equalsIgnoreCase("multi")) { doku = SudokuFactory.createBasicXShapedMultidoku(width, height, listConstraints); - } - else { + } else { doku = SudokuFactory.createBasicEmptyRectangleDoku(width, height, listConstraints); } System.out.println("Your sudoku will look like this:"); showMultidoku(doku, listSymbols, width, height); - System.out.println("We now will fill this sudoku."); + System.out.println( + "You can now manually fill this sudoku ('fill'), or generate a playable one ('generate', default):"); + if (reader.next().equalsIgnoreCase("fill")) { + findSolution(doku, listSymbols, width, height); + } else { + playableDoku(doku, listSymbols, width, height); + } + } + + private void playableDoku(MultiDoku doku, List listSymbols, int width, int height) { + System.out.println("We will now fill this sudoku."); System.out.println("What level of difficulty would you like?" + " ('very easy', 'easy', 'medium' (default), 'hard', 'full' (sudoku fully completed))"); String difficulty = reader.next().toLowerCase(); if (difficulty.equals("full")) { generateFullDoku(doku); - } - else { + System.out.println("Here's your sudoku !"); + exit(); + } else { generatePartialDoku(doku, difficulty); + System.out.println("Here's your sudoku !"); + showMultidoku(doku, listSymbols, width, height); + do { + turn(doku, listSymbols, width, height); + } while (!doku.isSolved()); + congrats(); } - System.out.println("Here's your sudoku !"); + } + + private void findSolution(MultiDoku doku, List listSymbols, int width, int height){ + do { + turn(doku, listSymbols, width, height); + } while (!doku.isSolved()); + System.out.println("This doku can be solved like this :"); showMultidoku(doku, listSymbols, width, height); - System.out.println("You can now save it!"); - saveMultiDoku(doku); + exit(); + + } + + private void congrats() { + System.out.println("Congrats! You've solved this sudoku! We hope this was fun! Let's play together again!"); + System.exit(0); } private MultiDoku saveChoice() { @@ -116,7 +144,8 @@ public class ConsoleInterface { } private List pickSymbols(int numberOfSymbols) { - System.out.println("Would you like to pick the " + numberOfSymbols + " symbols from the sudoku? (y/n, default 'no' will use numbers)"); + System.out.println("Would you like to pick the " + numberOfSymbols + + " symbols from the sudoku? (y/n, default 'no')"); if (reader.next().equalsIgnoreCase("y")) { List listSymbols = new ArrayList<>(); System.out.println("You have chosen to pick your own symbols."); @@ -132,7 +161,8 @@ public class ConsoleInterface { System.out.println("You chose the symbols: " + listSymbols.toString()); return listSymbols; } else { - System.out.println("What existing sets of symbols do you want to use? Numbers ('n', default), letters ('l'), or emojis ('e')?"); + System.out.println( + "What existing sets of symbols do you want to use? Numbers ('n', default), letters ('l'), or emojis ('e', may not work on all consoles)?"); return switch (reader.next().toLowerCase()) { case "l" -> Symbols.Letters.getSymbols(); case "e" -> Symbols.Emojis.getSymbols(); @@ -143,8 +173,9 @@ public class ConsoleInterface { private List getListConstraints() { List listConstraints = SudokuFactory.DEFAULT_CONSTRAINTS; - System.out.println("The sudoku have constraints of blocks, lines and columns. Would you like to add the diagonal constraints ? (y/n, default 'no')"); - if(reader.next().equalsIgnoreCase("y")){ + System.out.println( + "The sudoku have constraints of blocks, lines and columns. Would you like to add the diagonal constraints ? (y/n, default 'no')"); + if (reader.next().equalsIgnoreCase("y")) { listConstraints.add(Constraint.Diagonal); } return listConstraints; @@ -152,11 +183,15 @@ public class ConsoleInterface { private void generatePartialDoku(MultiDoku doku, String difficultyName) { Difficulty difficulty; - switch (difficultyName){ - case "very easy": difficulty = Difficulty.VeryEasy; - case "easy": difficulty = Difficulty.Easy; - case "hard": difficulty = Difficulty.Hard; - default: difficulty = Difficulty.Medium; + switch (difficultyName) { + case "very easy": + difficulty = Difficulty.VeryEasy; + case "easy": + difficulty = Difficulty.Easy; + case "hard": + difficulty = Difficulty.Hard; + default: + difficulty = Difficulty.Medium; } try { SudokuFactory.fillDoku(doku, difficulty); @@ -169,18 +204,102 @@ public class ConsoleInterface { new RandomSolver().solve(doku); } - private void showMultidoku(MultiDoku doku, List listSymbols, int width, int height){ + private void showMultidoku(MultiDoku doku, List listSymbols, int width, int height) { showMultiDoku(RenderableMultidoku.fromMultidoku(doku), listSymbols, width, height); } - private void showMultiDoku(RenderableMultidoku doku, List listSymbols, int width, int height){ - SudokuPrinter.printMultiDoku(doku, listSymbols, width, height); + private void showMultiDoku(RenderableMultidoku doku, List listSymbols, int width, int height) { + SudokuPrinter.printMultiDokuWithIndex(doku, listSymbols, width, height); } - private void saveMultiDoku(MultiDoku doku){ + private void saveMultiDoku(MultiDoku doku) { System.out.println("Number of the file to overwrite ('-1' or unused save file number to create a new save) :"); int n = reader.nextInt(); String path = SudokuSerializer.saveMultiDoku(doku, n); System.out.println("The path to your save is:" + path); } + + private void turn(MultiDoku doku, List listSymbols, int width, int height) { + System.out.println( + "You can now put a number in a cell ('play', default), save the state of the doku ('save'), show a solution ('solution') or exit the program ('exit')."); + switch (reader.next()) { + case "save": + saveMultiDoku(doku); + break; + case "solution": + solve(doku); + break; + case "exit": + exit(); + break; + default: + play(doku, listSymbols, width, height); + break; + } + } + + private void solve(MultiDoku doku){ + System.out.println("Pick a solver to use : random ('random', default), human ('human') or mixed solver ('mixed')."); + switch (reader.next()) { + case "human": + new HumanSolver().solve(doku); + break; + case "mixed": + new MixedSolver().solve(doku); + break; + default: + new RandomSolver().solve(doku); + break; + } + } + + private void play(MultiDoku doku, List listSymbols, int width, int height) { + int x, y; + RenderableMultidoku rdoku = RenderableMultidoku.fromMultidoku(doku); + do { + System.out.println("Line of the cell to fill:"); + y = reader.nextInt(); + System.out.println("Column of the cell to fill:"); + x = reader.nextInt(); + } while (!isValidCoordinates(rdoku, width, height, x-1, y-1)); + Cell cell = rdoku.getCell(x-1, y-1); + System.out.println("Character to put in the (" + x + ", " + y + ") cell:"); + String character = reader.next(); + while (!isValidSymbol(character, listSymbols, width * height)) { + System.out.println("This is not a valid symbol; try again:"); + character = reader.next(); + } + cell.setSymbolIndex(indexOfSymbol(character, listSymbols, width * height)); + showMultiDoku(rdoku, listSymbols, width, height); + } + + private boolean isValidCoordinates(RenderableMultidoku doku, int width, int height, int x, int y) { + if (doku.getCell(x, y) != null) { + return true; + } + return false; + } + + private int indexOfSymbol(String symbol, List listSymbols, int nbSymbols) { + for (int i = 0; i < nbSymbols; i++) { + if (listSymbols.get(i).equals(symbol)) { + return i; + } + } + return -1; + } + + private boolean isValidSymbol(String symbol, List listSymbols, int size) { + for (int i = 0; i < size; i++) { + if (listSymbols.get(i).equals(symbol)) { + return true; + } + } + return false; + } + + private void exit() { + System.out.println("Thank you for playing!"); + System.exit(0); + } } diff --git a/app/src/main/java/sudoku/io/SudokuPrinter.java b/app/src/main/java/sudoku/io/SudokuPrinter.java index 1d1c0d8..bc065a2 100644 --- a/app/src/main/java/sudoku/io/SudokuPrinter.java +++ b/app/src/main/java/sudoku/io/SudokuPrinter.java @@ -21,39 +21,73 @@ public class SudokuPrinter { printRectangleSudoku(s, blockWidth, blockHeight, symbols.getSymbols()); } - public static void printRectangleSudoku(final Sudoku s, int blockWidth, int blockHeight, List listSymbols){ + public static void printRectangleSudoku(final Sudoku s, int blockWidth, int blockHeight, List listSymbols) { for (int y = 0; y < s.getSize(); y++) { - if (y % blockHeight == 0 && y > 0) { - System.out.println(); + if (y % blockHeight == 0 && y > 0) { + System.out.println(); + } + StringBuilder line = new StringBuilder("[ "); + for (int x = 0; x < s.getSize(); x++) { + Cell c = s.getCell(x, y); + if (c.getSymbolIndex() == Cell.NOSYMBOL) { + line.append(" "); + } else { + line.append(listSymbols.get(c.getSymbolIndex())).append(" "); + } + if (x % blockWidth == blockWidth - 1 && x != blockWidth * blockHeight - 1) { + line.append("| "); + } + } + line.append("]"); + System.out.println(line); + } + } + + public static void printRectangleSudokuWithIndex(final Sudoku s, int blockWidth, int blockHeight, + List listSymbols) { + StringBuilder header = new StringBuilder(""); + header.append(" "); + for (int x = 0; x < blockWidth*blockHeight; x++) { + header.append(x + 1).append(" "); + if (x % blockWidth == blockWidth - 1 && x != blockWidth * blockHeight - 1) { + header.append(" "); + } + } + header.append("\n"); + System.out.println(header); + for (int y = 0; y < s.getSize(); y++) { + if (y % blockHeight == 0 && y > 0) { + System.out.println(); + } + StringBuilder line = new StringBuilder(y + 1); + line.append(" [ "); + for (int x = 0; x < s.getSize(); x++) { + Cell c = s.getCell(x, y); + if (c.getSymbolIndex() == Cell.NOSYMBOL) { + line.append(" "); + } else { + line.append(listSymbols.get(c.getSymbolIndex())).append(" "); + } + if (x % blockWidth == blockWidth - 1 && x != blockWidth * blockHeight - 1) { + line.append("| "); + } + } + line.append("]"); + System.out.println(line); } - StringBuilder line = new StringBuilder("[ "); - for (int x = 0; x < s.getSize(); x++) { - Cell c = s.getCell(x, y); - if (c.getSymbolIndex() == Cell.NOSYMBOL) { - line.append(" "); - } - else { - line.append(listSymbols.get(c.getSymbolIndex())).append(" "); - } - if (x % blockWidth == blockWidth - 1 && x != blockWidth * blockHeight - 1) { - line.append("| "); - } - } - line.append("]"); - System.out.println(line); } -} public static void printMultiDoku(final RenderableMultidoku rm, Symbols symbols, int blockWidth, int blockHeight) { printMultiDoku(rm, symbols.getSymbols(), blockWidth, blockHeight); } - public static void printMultiDoku(final RenderableMultidoku rm, List listSymbols, int blockWidth, int blockHeight) { + public static void printMultiDoku(final RenderableMultidoku rm, List listSymbols, int blockWidth, + int blockHeight) { StringBuilder line = new StringBuilder("\n"); int nBlockInWidth = rm.getWidth() / blockWidth; for (int y = 0; y < rm.getHeight(); y++) { if (y % blockHeight == 0) { - line.append("__".repeat(Math.max(0, rm.getWidth()+nBlockInWidth))).append("_\n"); + line.append("__".repeat(Math.max(0, rm.getWidth() + nBlockInWidth))).append("_\n"); } line.append("[ "); for (int x = 0; x < rm.getWidth(); x++) { @@ -64,26 +98,63 @@ public class SudokuPrinter { if (cell != null) { if (cell.getSymbolIndex() == Cell.NOSYMBOL) { line.append("- "); - } - else { + } else { line.append(listSymbols.get(cell.getSymbolIndex())).append(" "); } - } - else { + } else { line.append(" "); } } line.append("]\n"); } - line.append("__".repeat(Math.max(0, rm.getWidth()+nBlockInWidth))).append("_\n"); + line.append("__".repeat(Math.max(0, rm.getWidth() + nBlockInWidth))).append("_\n"); System.out.println(line); } - public static String toStringRectangleSudoku(final Sudoku s, int blockWidth, int blockHeight, Symbols symbols){ + public static void printMultiDokuWithIndex(final RenderableMultidoku rm, List listSymbols, int blockWidth, + int blockHeight) { + StringBuilder line = new StringBuilder("\n"); + line.append(" "); + for (int x = 0; x < rm.getWidth(); x++) { + line.append(x + 1).append(" "); + if (x % blockWidth == blockWidth - 1 && x != blockWidth * blockHeight - 1) { + line.append(" "); + } + } + line.append("\n"); + int nBlockInWidth = rm.getWidth() / blockWidth; + for (int y = 0; y < rm.getHeight(); y++) { + if (y % blockHeight == 0) { + line.append(" ").append("__".repeat(Math.max(0, rm.getWidth() + nBlockInWidth))).append("_\n"); + } + line.append(y+1).append(" [ "); + for (int x = 0; x < rm.getWidth(); x++) { + if (x % blockWidth == 0 && x > 0) { + line.append("| "); + } + Cell cell = rm.getCell(x, y); + if (cell != null) { + if (cell.getSymbolIndex() == Cell.NOSYMBOL) { + line.append("- "); + } else { + line.append(listSymbols.get(cell.getSymbolIndex())).append(" "); + } + } else { + line.append(" "); + } + } + line.append("]\n"); + } + line.append(" ").append("__".repeat(Math.max(0, rm.getWidth() + nBlockInWidth))).append("_\n"); + System.out.println(line); + } + + public static String toStringRectangleSudoku(final Sudoku s, int blockWidth, int blockHeight, Symbols symbols) { return toStringRectangleSudoku(s, blockWidth, blockHeight, symbols.getSymbols()); } - public static String toStringRectangleSudoku(final Sudoku s, int blockWidth, int blockHeight, List listSymbols) { + public static String toStringRectangleSudoku(final Sudoku s, int blockWidth, int blockHeight, + List listSymbols) { StringBuilder result = new StringBuilder(); for (int y = 0; y < s.getSize(); y++) { // Ajouter une ligne vide entre les blocs horizontaux @@ -96,8 +167,7 @@ public class SudokuPrinter { Cell cell = s.getCell(x, y); if (cell.getSymbolIndex() == Cell.NOSYMBOL) { line.append(" "); - } - else { + } else { line.append(listSymbols.get(cell.getSymbolIndex())).append(" "); } @@ -112,12 +182,21 @@ public class SudokuPrinter { return result.toString(); } - public static void printMultiDoku(final MultiDoku doku, int blockWidth, int blockHeight, Symbols symbols){ - if (doku.getNbSubGrids()==1) { + public static void printMultiDoku(final MultiDoku doku, int blockWidth, int blockHeight, Symbols symbols) { + if (doku.getNbSubGrids() == 1) { printRectangleSudoku(doku.getSubGrid(0), blockWidth, blockHeight, symbols); - } - else { + } else { printMultiDoku(RenderableMultidoku.fromMultidoku(doku), symbols, blockWidth, blockHeight); } } + + public static void printMultiDokuWithIndex(final MultiDoku doku, int blockWidth, int blockHeight, Symbols symbols) { + if (doku.getNbSubGrids() == 1) { + printRectangleSudokuWithIndex(doku.getSubGrid(0), blockWidth, blockHeight, symbols.getSymbols()); + } else { + printMultiDokuWithIndex(RenderableMultidoku.fromMultidoku(doku), symbols.getSymbols(), blockWidth, + blockHeight); + } + } + } diff --git a/app/src/main/java/sudoku/structure/MultiDoku.java b/app/src/main/java/sudoku/structure/MultiDoku.java index d817eb7..6b00947 100644 --- a/app/src/main/java/sudoku/structure/MultiDoku.java +++ b/app/src/main/java/sudoku/structure/MultiDoku.java @@ -81,9 +81,9 @@ public class MultiDoku { } /** - * Check si le MultiDoku est valide, en fonction de ses sous-Sudokus. + * Check si le MultiDoku est résolu, c'est à dire complet et cohérent avec ses contraintes. * - * @return boolean, true s'il est valide et false sinon. + * @return boolean, true s'il est résolu et false sinon. */ public boolean isSolved() { for (Sudoku sudoku : this.subGrids) { diff --git a/app/src/main/java/sudoku/structure/Sudoku.java b/app/src/main/java/sudoku/structure/Sudoku.java index 3458d40..0fe5416 100644 --- a/app/src/main/java/sudoku/structure/Sudoku.java +++ b/app/src/main/java/sudoku/structure/Sudoku.java @@ -245,7 +245,7 @@ public class Sudoku { } /** - * Vérifie si le Sudoku est résolue, soit complet et cohérent avec ses contraintes. + * Vérifie si le Sudoku est résolu, c'est à dire complet et cohérent avec ses contraintes. * @return boolean, valant true si le Sudoku est résolu, false sinon. */ public boolean isSolved() {