296 lines
7.4 KiB
Java
296 lines
7.4 KiB
Java
package sudoku.structure;
|
|
|
|
import sudoku.constraint.BlockConstraint;
|
|
import sudoku.constraint.Constraint;
|
|
import sudoku.constraint.IConstraint;
|
|
import sudoku.io.SudokuPrinter;
|
|
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
|
|
/**
|
|
* @class Sudoku
|
|
* @brief Représent un Sudoku
|
|
*/
|
|
public class Sudoku {
|
|
|
|
/**
|
|
* Liste des Block contenus dans le Sudoku.
|
|
*/
|
|
private final List<Block> blocks;
|
|
/**
|
|
* Liste des Cells contenus dans le Sudoku.
|
|
*/
|
|
private List<Cell> cells = new ArrayList<>();
|
|
/**
|
|
* Liste des contraintes (TODO) du Sudoku.
|
|
*/
|
|
private final List<Constraint> constraints;
|
|
/**
|
|
* Largeur des Blocks s'ils sont rectangulaires, valant 0 si ce n'est pas le cas.
|
|
*/
|
|
private int blockWidth;
|
|
|
|
public Sudoku(List<Cell> cells, List<Block> blocks, List<Constraint> constraints) {
|
|
this.cells = cells;
|
|
this.blocks = blocks;
|
|
this.constraints = constraints;
|
|
}
|
|
|
|
/**
|
|
* Transforme un index de Cell en Coordinate.
|
|
*
|
|
* @param index int, index d'une Cell.
|
|
* @return Coordinate, correspondante à l'index donné.
|
|
*/
|
|
public Coordinate toCoords(int index) {
|
|
return new Coordinate(index % getSize(), index / getSize());
|
|
}
|
|
|
|
/**
|
|
* Transforme des coordonées d'une Cell en index.
|
|
* @param x int, abscisse.
|
|
* @param y int, ordonnée.
|
|
* @return int, index correspondant.
|
|
*/
|
|
public int toIndex(int x, int y) {
|
|
return y * getSize() + x;
|
|
}
|
|
|
|
/**
|
|
* Vérifie que des coordonnées correspondent bien à une Cell dans le Sudoku.
|
|
* @return boolean, valant true si les coordonnées sont dans les bornes du Sudoku, false sinon.
|
|
*/
|
|
public boolean isValidCoords(int x, int y) {
|
|
int index = toIndex(x, y);
|
|
return isValidCoords(index);
|
|
}
|
|
|
|
/**
|
|
* Vérifie que l'index correspond bien à une Cell dans le Sudoku.
|
|
* @return boolean, valant true si l'index est dans les bornes du Sudoku, false sinon.
|
|
*/
|
|
public boolean isValidCoords(int index) {
|
|
return index < getSize() * getSize();
|
|
}
|
|
|
|
/**
|
|
* Teste si on peut placer la value dans la Cell aux coordonnées x, y d'après les contraintes du Sudoku.
|
|
* @param x int, abscisse de la Cell voulue.
|
|
* @param y int, ordonnée de la Cell voulue.
|
|
* @param value int, index du symbole qu'on veut placer.
|
|
* @return boolean, true si on peut la placer et false sinon.
|
|
*/
|
|
public boolean canBePlaced(int x, int y, int value) {
|
|
for (Constraint constraint : this.constraints) {
|
|
if (!constraint.canBePlaced(this, x, y, value)) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Vide la Cell dotn les coordonnées sont renseignées de son symbole.
|
|
* @param x int, abscisse de la Cell voulue.
|
|
* @param y int, coordonnée de la Cell voulue.
|
|
*/
|
|
|
|
public void clearCell(int x, int y) {
|
|
assert (isValidCoords(x, y));
|
|
Cell cell = getCell(x, y);
|
|
cell.setSymbolIndex(Cell.NOSYMBOL);
|
|
}
|
|
|
|
/**
|
|
* Vide toutes les Cell du Sudoku.
|
|
*/
|
|
public void clear() {
|
|
for (int i = 0; i < getSize() * getSize(); i++) {
|
|
Cell cell = getCell(i);
|
|
if (cell.isMutable())
|
|
cell.setSymbolIndex(Cell.NOSYMBOL);
|
|
}
|
|
}
|
|
|
|
public int getBlockWidth() {
|
|
return blockWidth;
|
|
}
|
|
|
|
/**
|
|
* Place le symbole d'index value dans la Cell de coordonnées précisées.
|
|
* @param x int, abscisse de la Cell voulue.
|
|
* @param y int, coordonnée de la Cell voulue.
|
|
* @param value int, index du symbole à placer.
|
|
* @return Cell, la Cell qui a été modifiée.
|
|
*/
|
|
public Cell setCellSymbol(int x, int y, int value) {
|
|
assert (isValidCoords(x, y));
|
|
for (Constraint constraint : this.constraints) {
|
|
if (!constraint.canBePlaced(this, x, y, value)) {
|
|
return null;
|
|
}
|
|
}
|
|
Cell cell = getCell(x, y);
|
|
cell.setSymbolIndex(value);
|
|
return cell;
|
|
}
|
|
|
|
/**
|
|
* Place les symboles d'index contenus dans values dans les cases du Sudoku.
|
|
* @param values List<Integer>, liste des index des symboles à placer.
|
|
* @return boolean, vaut true si les symboles ont été placés, false sinon.
|
|
*/
|
|
public boolean setCellsSymbol(List<Integer> 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;
|
|
}
|
|
|
|
/**
|
|
* Place les symboles d'index contenus dans values dans les cases du Sudoku et rend ces cases immuables.
|
|
* @param values List<Integer>, liste des index des symboles à placer.
|
|
* @return boolean, vaut true si les symboles ont été placés, false sinon.
|
|
*/
|
|
public boolean setImmutableCellsSymbol(List<Integer> 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);
|
|
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 = toIndex(x, y);
|
|
assert (isValidCoords(x, y));
|
|
try {
|
|
return this.cells.get(index);
|
|
} catch (IndexOutOfBoundsException e) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public Cell getCell(int i) {
|
|
return this.cells.get(i);
|
|
}
|
|
|
|
public List<Constraint> getConstraints() {
|
|
return constraints;
|
|
}
|
|
|
|
public int getSize() {
|
|
return this.blocks.size();
|
|
}
|
|
|
|
public List<Cell> getCells() {
|
|
return this.cells;
|
|
}
|
|
|
|
public List<Block> getBlocks() {
|
|
return this.blocks;
|
|
}
|
|
|
|
/**
|
|
* Vérifie si une Cell appartient au Sudoku.
|
|
* @param cell Cell, cellule dont on veut vérifier l'appartenance au Sudoku.
|
|
* @return boolean, vaut true si la Cell appartient au Sudoku.
|
|
*/
|
|
public boolean contains(Cell cell) {
|
|
return this.cells.contains(cell);
|
|
}
|
|
|
|
public String toString() {
|
|
StringBuilder sb = new StringBuilder();
|
|
sb.append("Sudoku {");
|
|
for (int i = 0; i < getSize(); i++) {
|
|
sb.append("\n\t");
|
|
for (int j = 0; j < getSize(); j++) {
|
|
Cell cell = getCell(i, j);
|
|
sb.append(cell.toString()).append(" ");
|
|
}
|
|
}
|
|
sb.append("\n}");
|
|
return sb.toString();
|
|
}
|
|
|
|
/**
|
|
* Renvoie la 1re Cell vide du Sudoku.
|
|
* @return Cell, une Cell vide, ou null s'il n'y en a pas.
|
|
*/
|
|
public Cell getFirstEmptyCell() {
|
|
for (Cell cell : this.cells) {
|
|
if (cell.isEmpty()) {
|
|
return cell;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Vérifie si le Sudoku est résolue, soit complet et cohérent avec ses contraintes.
|
|
* @return boolean, valant true si le Sudoku est résolu, false sinon.
|
|
*/
|
|
public boolean isSolved() {
|
|
boolean isComplete = isComplete();
|
|
boolean isValid = isValid();
|
|
return isComplete && isValid;
|
|
}
|
|
|
|
/**
|
|
* Vérifie que le Sudoku est complet, soit qu'il n'y ait aucune case vide.
|
|
* @return boolean, true si le Sudoku est complet, false sinon.
|
|
*/
|
|
private boolean isComplete() {
|
|
return getFirstEmptyCell() == null;
|
|
}
|
|
|
|
/**
|
|
* Vérifie si le Sudoku est valide, soit qu'il est cohérent avec ses contraintes.
|
|
* @return bollean, true si le Sudoku est valide, false sinon
|
|
*/
|
|
private boolean isValid() {
|
|
for (int i = 0; i < cells.size(); i++) {
|
|
Cell cell = getCell(i);
|
|
if (cell.isEmpty())
|
|
continue;
|
|
|
|
Coordinate coordinate = toCoords(i);
|
|
|
|
int symbolPlaced = cell.empty();
|
|
if (!canBePlaced(coordinate.getX(), coordinate.getY(), symbolPlaced)) {
|
|
cell.setSymbolIndex(symbolPlaced);
|
|
return false;
|
|
}
|
|
cell.setSymbolIndex(symbolPlaced);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public void setBlockWidth(int blockWidth) {
|
|
this.blockWidth = blockWidth;
|
|
}
|
|
|
|
public boolean hasConstraint(Constraint constraint) {
|
|
return this.constraints.contains(constraint);
|
|
}
|
|
|
|
}
|