package gui; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import sudoku.structure.Block; import sudoku.structure.Cell; import sudoku.structure.Coordinate; import sudoku.structure.MultiDoku; import sudoku.structure.Sudoku; public class RenderableMultidoku { private final MultiDoku doku; private final List cells; private final List blocks; private final int width; private final int height; private RenderableMultidoku(MultiDoku doku, List blocks, List cells, int width, int height) { this.cells = cells; this.blocks = blocks; this.width = width; this.height = height; this.doku = doku; } public boolean isResolved() { return this.doku.isValid(); } public int getWidth() { return width; } public int getHeight() { return height; } public List getBlocks() { return this.blocks; } public Cell getCell(int x, int y) { return getCell(y * width + x); } public Cell getCell(int index) { return cells.get(index); } public boolean setCellValue(Cell cell, int value) { for (Sudoku s : doku.getSubGrids()) { int cellIndex = s.getCells().indexOf(cell); // la cellule existe if (cellIndex != -1) { int cellX = cellIndex % s.getSize(); int cellY = cellIndex / s.getSize(); if (!s.canBePlaced(cellX, cellY, value)) { return false; } } } cell.setSymbolIndex(value); return true; } private static record PositionConstraint(Sudoku sudoku1, Sudoku sudoku2, Coordinate offset) { } private static Coordinate getConstraint(Sudoku sudoku1, Sudoku sudoku2) { int blockWidth = sudoku1.getBlockWidth(); int blockHeight = sudoku1.getSize() / blockWidth; for (int i = 0; i < sudoku1.getSize(); i++) { for (int j = 0; j < sudoku2.getSize(); j++) { Block block1 = sudoku1.getBlocks().get(i); Block block2 = sudoku2.getBlocks().get(j); if (block1 == block2) { int block1X = i % blockHeight; int block1Y = i / blockHeight; int block2X = j % blockHeight; int block2Y = j / blockHeight; return new Coordinate((block1X - block2X) * blockWidth, (block1Y - block2Y) * blockHeight); } } } return null; } private static boolean hasContraint(Sudoku sudoku1, Sudoku sudoku2, List positionConstraints) { for (PositionConstraint constraint : positionConstraints) { if ((constraint.sudoku1 == sudoku1 && constraint.sudoku2 == sudoku2) || (constraint.sudoku1 == sudoku2 && constraint.sudoku2 == sudoku1)) return true; } return false; } private static Coordinate getMinSudokuOffset(Map sudokusOffset) { Coordinate minCoordinate = null; for (Coordinate coordinate : sudokusOffset.values()) { if (minCoordinate == null) minCoordinate = coordinate; minCoordinate = new Coordinate(Math.min(minCoordinate.getX(), coordinate.getX()), Math.min(minCoordinate.getY(), coordinate.getY())); } return minCoordinate; } private static Coordinate getMaxSudokuCoordinate(Map sudokusOffset) { Coordinate maxCoordinate = null; Sudoku maxSudoku = null; float maxDistanceSquared = 0; for (var entry : sudokusOffset.entrySet()) { Coordinate coordinate = entry.getValue(); float distanceSquared = coordinate.getX() * coordinate.getX() + coordinate.getY() * coordinate.getY(); if (maxCoordinate == null) { maxCoordinate = coordinate; maxDistanceSquared = distanceSquared; maxSudoku = entry.getKey(); } if (distanceSquared > maxDistanceSquared) { maxDistanceSquared = distanceSquared; maxSudoku = entry.getKey(); maxCoordinate = coordinate; } } int blockWidth = maxSudoku.getBlockWidth(); int blockHeight = maxSudoku.getSize() / blockWidth; return new Coordinate(maxCoordinate.getX() + maxSudoku.getSize(), maxCoordinate.getY() + maxSudoku.getSize()); } public static RenderableMultidoku fromMultidoku(MultiDoku doku) { if (doku.getNbSubGrids() == 1) { Sudoku sudoku = doku.getSubGrid(0); return new RenderableMultidoku(doku, sudoku.getBlocks(), sudoku.getCells(), sudoku.getSize(), sudoku.getSize()); } Map sudokusOffset = new HashMap<>(); // coordinates in cell List positionConstraints = new ArrayList<>(); for (Sudoku sudoku1 : doku.getSubGrids()) { for (Sudoku sudoku2 : doku.getSubGrids()) { if (sudoku1 == sudoku2) continue; Coordinate constraint = getConstraint(sudoku1, sudoku2); if (constraint != null && !hasContraint(sudoku1, sudoku2, positionConstraints)) { positionConstraints.add(new PositionConstraint(sudoku1, sudoku2, constraint)); } } } for (PositionConstraint constraint : positionConstraints) { if (!sudokusOffset.containsKey(constraint.sudoku1) && !sudokusOffset.containsKey(constraint.sudoku2)) { sudokusOffset.put(constraint.sudoku1, new Coordinate(0, 0)); sudokusOffset.put(constraint.sudoku2, constraint.offset()); } if (sudokusOffset.containsKey(constraint.sudoku1)) { sudokusOffset.put(constraint.sudoku2, constraint.offset.add(sudokusOffset.get(constraint.sudoku1))); } else { sudokusOffset.put(constraint.sudoku1, constraint.offset.add(sudokusOffset.get(constraint.sudoku2))); } } Coordinate minCoordinate = getMinSudokuOffset(sudokusOffset); for (var entry : sudokusOffset.entrySet()) { entry.setValue(entry.getValue().sub(minCoordinate)); } Coordinate maxCoordinate = getMaxSudokuCoordinate(sudokusOffset); List blocks = new ArrayList<>(); List cells = new ArrayList<>(maxCoordinate.getX() * maxCoordinate.getY()); for (int i = 0; i < maxCoordinate.getX() * maxCoordinate.getY(); i++) { cells.add(null); } for (var entry : sudokusOffset.entrySet()) { Sudoku sudoku = entry.getKey(); Coordinate offset = entry.getValue(); for (int x = 0; x < sudoku.getSize(); x++) { for (int y = 0; y < sudoku.getSize(); y++) { Cell cell = sudoku.getCell(x, y); int absoluteX = x + offset.getX(); int absoluteY = y + offset.getY(); cells.set(absoluteY * maxCoordinate.getX() + absoluteX, cell); } } for (Block block : sudoku.getBlocks()) { if (!blocks.contains(block)) { blocks.add(block); } } } return new RenderableMultidoku(doku, blocks, cells, maxCoordinate.getX(), maxCoordinate.getY()); } public MultiDoku getDoku() { return doku; } }