Drawer

Game

public class Cell {
    private int value;
    private boolean isFixed;

    public Cell() {
        this(0, false);
    }

    public Cell(int value) {
        this(value, value != 0);
    }

    public Cell(int value, boolean isFixed) {
        validateValue(value);
        this.value = value;
        this.isFixed = isFixed;
    }

    public int getValue() {
        return value;
    }

    public int get() {
        return getValue();
    }

    public boolean isFixed() {
        return isFixed;
    }

    public boolean getIsFixed() {
        return isFixed();
    }

    public void setFixed(boolean fixed) {
        isFixed = fixed;
    }

    public boolean setValue(int value) {
        validateValue(value);

        if (isFixed) {
            return false;
        }

        this.value = value;
        return true;
    }

    public boolean set(int value) {
        return setValue(value);
    }

    public boolean forceSetValue(int value) {
        validateValue(value);
        this.value = value;
        return true;
    }

    public boolean clear() {
        return setValue(0);
    }

    private void validateValue(int value) {
        if (value < 0 || value > 9) {
            throw new IllegalArgumentException(
                "A Sudoku cell value must be between 0 and 9."
            );
        }
    }

    @Override
    public String toString() {
        return Integer.toString(value);
    }
}

=====================================================================


public class Row {
    private final Cell[] cells;

    public Row() {
        this(createEmptyCells());
    }

    public Row(Cell[] cells) {
        validateCells(cells);
        this.cells = cells;
    }

    public Cell getCell(int column) {
        validateIndex(column);
        return cells[column];
    }

    public int get(int column) {
        return getCell(column).getValue();
    }

    public boolean set(int column, int value) {
        validateIndex(column);
        return cells[column].setValue(value);
    }

    public boolean clear(int column) {
        validateIndex(column);
        return cells[column].clear();
    }

    public boolean contains(int value) {
        if (value == 0) {
            return false;
        }

        for (Cell cell : cells) {
            if (cell.getValue() == value) {
                return true;
            }
        }

        return false;
    }

    public boolean isValid() {
        boolean[] seen = new boolean[10];

        for (Cell cell : cells) {
            int value = cell.getValue();

            if (value == 0) {
                continue;
            }

            if (seen[value]) {
                return false;
            }

            seen[value] = true;
        }

        return true;
    }

    public Cell[] getCells() {
        return cells.clone();
    }

    private static Cell[] createEmptyCells() {
        Cell[] cells = new Cell[9];

        for (int i = 0; i < cells.length; i++) {
            cells[i] = new Cell();
        }

        return cells;
    }

    private void validateIndex(int index) {
        if (index < 0 || index >= 9) {
            throw new IndexOutOfBoundsException(
                "Column index must be between 0 and 8."
            );
        }
    }

    private void validateCells(Cell[] cells) {
        if (cells == null || cells.length != 9) {
            throw new IllegalArgumentException(
                "A Sudoku row must contain exactly 9 cells."
            );
        }

        for (Cell cell : cells) {
            if (cell == null) {
                throw new IllegalArgumentException(
                    "A Sudoku row cannot contain null cells."
                );
            }
        }
    }
}



================================================================

public class Column {
    private final Cell[] cells;

    public Column(Cell[] cells) {
        validateCells(cells);
        this.cells = cells;
    }

    public Cell getCell(int row) {
        validateIndex(row);
        return cells[row];
    }

    public int get(int row) {
        return getCell(row).getValue();
    }

    public boolean set(int row, int value) {
        validateIndex(row);
        return cells[row].setValue(value);
    }

    public boolean clear(int row) {
        validateIndex(row);
        return cells[row].clear();
    }

    public boolean contains(int value) {
        if (value == 0) {
            return false;
        }

        for (Cell cell : cells) {
            if (cell.getValue() == value) {
                return true;
            }
        }

        return false;
    }

    public boolean isValid() {
        boolean[] seen = new boolean[10];

        for (Cell cell : cells) {
            int value = cell.getValue();

            if (value == 0) {
                continue;
            }

            if (seen[value]) {
                return false;
            }

            seen[value] = true;
        }

        return true;
    }

    public Cell[] getCells() {
        return cells.clone();
    }

    private void validateIndex(int index) {
        if (index < 0 || index >= 9) {
            throw new IndexOutOfBoundsException(
                "Row index must be between 0 and 8."
            );
        }
    }

    private void validateCells(Cell[] cells) {
        if (cells == null || cells.length != 9) {
            throw new IllegalArgumentException(
                "A Sudoku column must contain exactly 9 cells."
            );
        }

        for (Cell cell : cells) {
            if (cell == null) {
                throw new IllegalArgumentException(
                    "A Sudoku column cannot contain null cells."
                );
            }
        }
    }
}

=============================================================

public class Block {
    private final Cell[] cells;

    public Block(Cell[] cells) {
        validateCells(cells);
        this.cells = cells;
    }

    public Cell getCell(int index) {
        validateIndex(index);
        return cells[index];
    }

    public int get(int index) {
        return getCell(index).getValue();
    }

    public boolean set(int index, int value) {
        validateIndex(index);
        return cells[index].setValue(value);
    }

    public boolean clear(int index) {
        validateIndex(index);
        return cells[index].clear();
    }

    public boolean contains(int value) {
        if (value == 0) {
            return false;
        }

        for (Cell cell : cells) {
            if (cell.getValue() == value) {
                return true;
            }
        }

        return false;
    }

    public boolean isValid() {
        boolean[] seen = new boolean[10];

        for (Cell cell : cells) {
            int value = cell.getValue();

            if (value == 0) {
                continue;
            }

            if (seen[value]) {
                return false;
            }

            seen[value] = true;
        }

        return true;
    }

    public Cell[] getCells() {
        return cells.clone();
    }

    private void validateIndex(int index) {
        if (index < 0 || index >= 9) {
            throw new IndexOutOfBoundsException(
                "Block-cell index must be between 0 and 8."
            );
        }
    }

    private void validateCells(Cell[] cells) {
        if (cells == null || cells.length != 9) {
            throw new IllegalArgumentException(
                "A Sudoku block must contain exactly 9 cells."
            );
        }

        for (Cell cell : cells) {
            if (cell == null) {
                throw new IllegalArgumentException(
                    "A Sudoku block cannot contain null cells."
                );
            }
        }
    }
}

===========================================================

public class Board {
    private final Cell[][] cells;
    private final Row[] rows;
    private final Column[] columns;
    private final Block[] blocks;

    public Board() {
        this(new int[9][9]);
    }

    public Board(int[][] puzzle) {
        validatePuzzle(puzzle);

        cells = new Cell[9][9];
        rows = new Row[9];
        columns = new Column[9];
        blocks = new Block[9];

        createCells(puzzle);
        createRows();
        createColumns();
        createBlocks();
    }

    public Cell getCell(int row, int column) {
        validatePosition(row, column);
        return cells[row][column];
    }

    public int get(int row, int column) {
        return getCell(row, column).getValue();
    }

    public boolean set(int row, int column, int value) {
        validatePosition(row, column);
        return cells[row][column].setValue(value);
    }

    public boolean clear(int row, int column) {
        validatePosition(row, column);
        return cells[row][column].clear();
    }

    public boolean safeSet(int row, int column, int value) {
        validatePosition(row, column);
        validateValue(value);

        Cell target = cells[row][column];

        if (target.isFixed()) {
            return false;
        }

        if (value == 0) {
            return target.clear();
        }

        target.forceSetValue(0);

        int blockIndex = getBlockIndex(row, column);

        boolean legal =
            !rows[row].contains(value)
            && !columns[column].contains(value)
            && !blocks[blockIndex].contains(value);

        if (legal) {
            target.forceSetValue(value);
            return true;
        }

        target.forceSetValue(0);
        return false;
    }

    public Row getRow(int index) {
        validateIndex(index, "Row");
        return rows[index];
    }

    public Column getColumn(int index) {
        validateIndex(index, "Column");
        return columns[index];
    }

    public Block getBlock(int index) {
        validateIndex(index, "Block");
        return blocks[index];
    }

    public boolean isValid() {
        for (int i = 0; i < 9; i++) {
            if (!rows[i].isValid()
                    || !columns[i].isValid()
                    || !blocks[i].isValid()) {
                return false;
            }
        }

        return true;
    }

    public boolean isComplete() {
        if (!isValid()) {
            return false;
        }

        for (int row = 0; row < 9; row++) {
            for (int column = 0; column < 9; column++) {
                if (cells[row][column].getValue() == 0) {
                    return false;
                }
            }
        }

        return true;
    }

    public int getBlockIndex(int row, int column) {
        validatePosition(row, column);
        return (row / 3) * 3 + (column / 3);
    }

    private void createCells(int[][] puzzle) {
        for (int row = 0; row < 9; row++) {
            for (int column = 0; column < 9; column++) {
                int value = puzzle[row][column];

                cells[row][column] =
                    new Cell(value, value != 0);
            }
        }
    }

    private void createRows() {
        for (int row = 0; row < 9; row++) {
            rows[row] = new Row(cells[row]);
        }
    }

    private void createColumns() {
        for (int column = 0; column < 9; column++) {
            Cell[] columnCells = new Cell[9];

            for (int row = 0; row < 9; row++) {
                columnCells[row] = cells[row][column];
            }

            columns[column] = new Column(columnCells);
        }
    }

    private void createBlocks() {
        for (int block = 0; block < 9; block++) {
            Cell[] blockCells = new Cell[9];

            int startingRow = (block / 3) * 3;
            int startingColumn = (block % 3) * 3;
            int index = 0;

            for (int row = startingRow;
                    row < startingRow + 3;
                    row++) {

                for (int column = startingColumn;
                        column < startingColumn + 3;
                        column++) {

                    blockCells[index] = cells[row][column];
                    index++;
                }
            }

            blocks[block] = new Block(blockCells);
        }
    }

    private void validatePuzzle(int[][] puzzle) {
        if (puzzle == null || puzzle.length != 9) {
            throw new IllegalArgumentException(
                "The puzzle must contain exactly 9 rows."
            );
        }

        for (int row = 0; row < 9; row++) {
            if (puzzle[row] == null
                    || puzzle[row].length != 9) {

                throw new IllegalArgumentException(
                    "Every puzzle row must contain exactly 9 values."
                );
            }

            for (int column = 0; column < 9; column++) {
                validateValue(puzzle[row][column]);
            }
        }
    }

    private void validatePosition(int row, int column) {
        validateIndex(row, "Row");
        validateIndex(column, "Column");
    }

    private void validateIndex(int index, String name) {
        if (index < 0 || index >= 9) {
            throw new IndexOutOfBoundsException(
                name + " index must be between 0 and 8."
            );
        }
    }

    private void validateValue(int value) {
        if (value < 0 || value > 9) {
            throw new IllegalArgumentException(
                "A Sudoku value must be between 0 and 9."
            );
        }
    }

    @Override
    public String toString() {
        StringBuilder result = new StringBuilder();

        for (int row = 0; row < 9; row++) {
            if (row % 3 == 0) {
                result.append("-----------------------\n");
            }

            for (int column = 0; column < 9; column++) {
                result.append(
                    cells[row][column].getValue()
                ).append(' ');

                if (column % 3 == 2) {
                    result.append("| ");
                }
            }

            result.append('\n');
        }

        result.append("-----------------------");
        return result.toString();
    }
}