package sudoku.io; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import org.json.JSONArray; import org.json.JSONObject; import sudoku.constraint.Constraint; import sudoku.constraint.IConstraint; import sudoku.structure.Block; import sudoku.structure.Cell; import sudoku.structure.MultiDoku; import sudoku.structure.Sudoku; /** * Classe permettant d'effectuer des opérations sur les sudokus afin de les * charger/sauvegarder */ public class SudokuSerializer { /** * Convertit un sudoku en object JSON * @param multidoku le sudoku à sérialiser * @return le JSON */ public static JSONObject serializeSudoku(final MultiDoku multidoku) { List cellIds = new ArrayList<>(); List blockIds = new ArrayList<>(); JSONObject jsonRoot = new JSONObject(); JSONArray jsonCells = new JSONArray(); JSONArray jsonBlocks = new JSONArray(); JSONArray jsonSudokus = new JSONArray(multidoku.getNbSubGrids()); for (int i = 0; i < multidoku.getNbSubGrids(); i++) { Sudoku sudoku = multidoku.getSubGrid(i); // init cells for (Cell cell : sudoku.getCells()) { if (cellIds.contains(cell)) continue; cellIds.add(cell); Block block = cell.getBlock(); if (!blockIds.contains(block)) { blockIds.add(block); } int blockID = blockIds.indexOf(block); assert (blockID >= 0); int symbolIndex = cell.getSymbolIndex(); JSONObject cellJsonObject = new JSONObject(); cellJsonObject.put("blockID", blockID); cellJsonObject.put("symbolIndex", symbolIndex); if (!cell.isMutable()) { cellJsonObject.put("immutable", true); } jsonCells.put(cellJsonObject); } } // init blocks for (Block blockId : blockIds) { JSONObject blockJsonObject = new JSONObject(); JSONArray cellsJsonArray = new JSONArray(); for (Cell cell : blockId.getCells()) { int cellID = cellIds.indexOf(cell); assert (cellID >= 0); cellsJsonArray.put(cellID); } blockJsonObject.put("cellIDs", cellsJsonArray); jsonBlocks.put(blockJsonObject); } for (int i = 0; i < multidoku.getNbSubGrids(); i++) { // serialise sub grid JSONObject jsonSudoku = new JSONObject(); JSONArray cellsJsonArray = new JSONArray(); JSONArray blocksJsonArray = new JSONArray(); JSONArray constraintsJsonArray = new JSONArray(); Sudoku sudoku = multidoku.getSubGrid(i); // serialize cells for (Cell cell : sudoku.getCells()) { int cellID = cellIds.indexOf(cell); assert (cellID >= 0); cellsJsonArray.put(cellID); } // serialize blocks for (Block block : sudoku.getBlocks()) { int blockID = blockIds.indexOf(block); assert (blockID >= 0); blocksJsonArray.put(blockID); } // serialize constraints for (IConstraint cons : sudoku.getConstraints()) { boolean constraintSerialized = false; for (Constraint enumCons : Constraint.values()) { if (cons.getClass().isAssignableFrom(enumCons.getConstraint().getClass())) { constraintSerialized = true; constraintsJsonArray.put(enumCons.ordinal()); } } if (!constraintSerialized) { System.out.println("La contrainte " + cons.getClass() + " n'a pas pu être sérialisé !"); } } jsonSudoku.put("constraints", constraintsJsonArray); jsonSudoku.put("cells", cellsJsonArray); jsonSudoku.put("blocks", blocksJsonArray); jsonSudoku.put("blockWidth", sudoku.getBlockWidth()); jsonSudokus.put(i, jsonSudoku); } jsonRoot.put("multidoku", jsonSudokus); jsonRoot.put("cells", jsonCells); jsonRoot.put("blocks", jsonBlocks); return jsonRoot; } /** * Crée le répertoire save afin d'y sauvegarder les sudokus */ private static void createSaveDir() { File f = new File("save"); f.mkdir(); } /** * Save a serialized MultiDoku in a JSON file. * * @param doku MultiDoku, MultiDoku to save. * @return String, the path of the save. */ public static String saveMultiDoku(final MultiDoku doku) { createSaveDir(); JSONObject jsonRoot = serializeSudoku(doku); File f = new File("save", "save.json"); int i = 0; while (f.exists()) { String newName = "save-" + ++i + ".json"; f = new File("save", newName); } try (FileWriter file = new FileWriter(f)) { file.write(jsonRoot.toString(3)); } catch (IOException e) { e.printStackTrace(); } return f.getAbsolutePath(); } public static String saveMultiDoku(final MultiDoku doku, final int saveToOverwrite) { createSaveDir(); File f; if (saveToOverwrite == 0) { f = new File("save", "save.json"); } else { f = new File("save", "save-" + saveToOverwrite + ".json"); } if (!f.exists()) { return saveMultiDoku(doku); } else { try (FileWriter file = new FileWriter(f)) { file.write(serializeSudoku(doku).toString(3)); } catch (IOException e) { e.printStackTrace(); } return f.getAbsolutePath(); } } /** * Get a MultiDoku from a pre-existing json save file. * * @param numberSave int, number of the save file to open. * @return MultiDoku, MultiDoku contained in the file. * @throws Exception when the given save file does not exist. */ public static MultiDoku getSavedMultiDoku(int numberSave) throws Exception { String fileName; if (numberSave < 1) { fileName = "save.json"; } else { fileName = "save-" + numberSave + ".json"; } File f = new File("save", fileName); String fileContent; if (!f.exists()) { throw new Exception("This save does not exist."); } else { fileContent = new String(Files.readAllBytes(Paths.get("save/" + fileName))); return deserializeSudoku(fileContent); } } /** * Construit un sudoku à partir d'un String en JSON * @param json le sudoku sérialisé * @return le sudoku désérialisé */ public static MultiDoku deserializeSudoku(final String json) { JSONObject jsonRoot = new JSONObject(json); return deserializeSudoku(jsonRoot); } /** * Désérialise un sudoku d'un objet JSON * @param jsonObject l'objet à désérialiser * @return le sudoku correspondant */ public static MultiDoku deserializeSudoku(final JSONObject jsonObject) { List cells = new ArrayList<>(); List blocks = new ArrayList<>(); List sudokus = new ArrayList<>(); // init cells JSONArray cellsJson = jsonObject.getJSONArray("cells"); for (int i = 0; i < cellsJson.length(); i++) { JSONObject entry = cellsJson.getJSONObject(i); int symbolIndex = entry.getInt("symbolIndex"); if (entry.has("immutable")) { cells.add(new Cell(symbolIndex, false)); } else { cells.add(new Cell(symbolIndex)); } } // init blocks JSONArray blocksJson = jsonObject.getJSONArray("blocks"); for (int i = 0; i < blocksJson.length(); i++) { JSONObject entry = blocksJson.getJSONObject(i); JSONArray cellIds = entry.getJSONArray("cellIDs"); List blockCells = new ArrayList<>(); Block newBlock = new Block(blockCells); for (int j = 0; j < cellIds.length(); j++) { int cellID = cellIds.getInt(j); Cell blockCell = cells.get(cellID); blockCell.setBlock(newBlock); blockCells.add(blockCell); } blocks.add(newBlock); } JSONArray multidokuJsonObject = jsonObject.getJSONArray("multidoku"); for (int i = 0; i < multidokuJsonObject.length(); i++) { JSONObject sudokuJsonObject = multidokuJsonObject.getJSONObject(i); JSONArray sudokuCellsJsonArray = sudokuJsonObject.getJSONArray("cells"); JSONArray sudokuBlocksJsonArray = sudokuJsonObject.getJSONArray("blocks"); JSONArray sudokuConstraintsJsonArray = sudokuJsonObject.getJSONArray("constraints"); List sudokuCells = new ArrayList<>(); List sudokuBlocks = new ArrayList<>(); List sudokuConstraints = new ArrayList<>(); for (int j = 0; j < sudokuCellsJsonArray.length(); j++) { int cellID = sudokuCellsJsonArray.getInt(j); sudokuCells.add(cells.get(cellID)); } for (int j = 0; j < sudokuBlocksJsonArray.length(); j++) { int blockID = sudokuBlocksJsonArray.getInt(j); sudokuBlocks.add(blocks.get(blockID)); } for (int j = 0; j < sudokuConstraintsJsonArray.length(); j++) { int constraintID = sudokuConstraintsJsonArray.getInt(j); sudokuConstraints.add(Constraint.values()[constraintID].getConstraint()); } Sudoku s = new Sudoku(sudokuCells, sudokuBlocks, sudokuConstraints); s.setBlockWidth(sudokuJsonObject.getInt("blockWidth")); for (Block block : s.getBlocks()) { block.getSudokus().add(s); } sudokus.add(s); } return new MultiDoku(sudokus); } }