package sudoku.io; import gui.RenderableMultidoku; import gui.Symbols; import sudoku.constraint.*; import sudoku.solver.*; import sudoku.structure.*; import java.util.ArrayList; import java.util.List; import java.util.Scanner; public class ConsoleInterface { public Scanner reader = new Scanner(System.in); 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")) { useSavedDoku(); } else { createDoku(); } } private void useSavedDoku() { System.out.println("What save should we use? Please enter the save number."); MultiDoku md = saveChoice(); int blockWidth = md.getSubGrid(0).getBlockWidth(); int blockHeight = md.getSubGrid(0).getBlocks().getFirst().getCells().size() / blockWidth; 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() { 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(); System.out.println("Your sudoku will have blocks of a " + width + " x " + height + " format."); int numberOfSymbols = width * height; List listSymbols = pickSymbols(numberOfSymbols); List listConstraints = getListConstraints(); System.out.println("Now that we have the size of our sudoku, " + "would you rather have a single grid ('one', default), " + "or a a multidoku composed of 5 subgrids ('multi') ?"); MultiDoku doku; if (reader.next().equalsIgnoreCase("multi")) { doku = SudokuFactory.createBasicXShapedMultidoku(width, height, listConstraints); } else { doku = SudokuFactory.createBasicEmptyRectangleDoku(width, height, listConstraints); } System.out.println("Your sudoku will look like this:"); showMultidoku(doku, listSymbols, width, height); 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); 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(); } } 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); 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() { int nbSave; MultiDoku md = null; do { nbSave = reader.nextInt(); try { md = SudokuSerializer.getSavedMultiDoku(nbSave); } catch (Exception e) { System.out.println("There seems to be a problem with this save, please try again."); } } while (md == null); return md; } public int getBlockWidth() { System.out.println("Width of a block: "); int widthBlock = reader.nextInt(); checkValidSize(widthBlock); while (!checkValidSize(widthBlock)) { System.out.println("That is not a valid width for a block. Try again:"); widthBlock = reader.nextInt(); } System.out.println("You have chose a width of " + widthBlock + "."); return widthBlock; } public int getBlockHeight() { System.out.println("Height of a block: "); int heightBlock = reader.nextInt(); checkValidSize(heightBlock); while (!checkValidSize(heightBlock)) { System.out.println("That is not a valid height for a block. Try again:"); heightBlock = reader.nextInt(); } System.out.println("You have chose a height of " + heightBlock + "."); return heightBlock; } private Boolean checkValidSize(int size) { return (size > 0); } private List pickSymbols(int numberOfSymbols) { 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."); for (int i = 0; i < numberOfSymbols; i++) { System.out.println("Choose for the symbol number " + i + ": "); String newSymbol = reader.next(); while (listSymbols.contains(newSymbol)) { System.out.println("This symbol has already been given. Try again:"); newSymbol = reader.next(); } listSymbols.add(newSymbol); } 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', may not work on all consoles)?"); return switch (reader.next().toLowerCase()) { case "l" -> Symbols.Letters.getSymbols(); case "e" -> Symbols.Emojis.getSymbols(); default -> Symbols.Numbers.getSymbols(); }; } } 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")) { listConstraints.add(Constraint.Diagonal); } return listConstraints; } 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; } try { SudokuFactory.fillDoku(doku, difficulty); } catch (Exception e) { System.out.println("There seems to be a problem with those settings. Let's start again."); } } private void generateFullDoku(MultiDoku doku) { new RandomSolver().solve(doku); } 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.printMultiDokuWithIndex(doku, listSymbols, width, height); } 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); } }