← Назад к вопросам
Как бы создал сервис для проверки правильности результатов судоку
2.0 Middle🔥 101 комментариев
#Основы Java
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Сервис для проверки правильности результатов судоку
Разберу, как я бы создал такой сервис, используя чистую архитектуру.
Анализ задачи
Судоку — это сетка 9×9, где нужно проверить три условия:
- Строки (rows) — каждая строка содержит числа 1-9 без повторений
- Столбцы (columns) — каждый столбец содержит числа 1-9 без повторений
- Блоки 3×3 — каждый блок содержит числа 1-9 без повторений
Архитектурный подход
Presentation Layer (REST Controller)
↓
Application Layer (Service)
↓
Domain Layer (SudokuValidator)
Реализация
1. Domain Model
public class SudokuBoard {
private static final int SIZE = 9;
private static final int BLOCK_SIZE = 3;
private final int[][] board;
public SudokuBoard(int[][] board) {
if (board == null || board.length != SIZE) {
throw new IllegalArgumentException("Board must be 9x9");
}
this.board = board;
}
public int[][] getBoard() {
return board;
}
public int getSize() {
return SIZE;
}
public int getBlockSize() {
return BLOCK_SIZE;
}
public int getValue(int row, int col) {
return board[row][col];
}
}
public class SudokuValidationResult {
private final boolean valid;
private final String message;
private final String errorLocation; // Где ошибка (строка, столбец или блок)
public SudokuValidationResult(boolean valid, String message, String errorLocation) {
this.valid = valid;
this.message = message;
this.errorLocation = errorLocation;
}
public boolean isValid() {
return valid;
}
public String getMessage() {
return message;
}
public String getErrorLocation() {
return errorLocation;
}
public static SudokuValidationResult valid() {
return new SudokuValidationResult(true, "Судоку решено правильно", null);
}
public static SudokuValidationResult invalid(String message, String location) {
return new SudokuValidationResult(false, message, location);
}
}
2. Domain Service (Validator)
import java.util.HashSet;
import java.util.Set;
@Component
public class SudokuValidator {
public SudokuValidationResult validate(SudokuBoard board) {
// Проверяем строки
for (int row = 0; row < board.getSize(); row++) {
if (!isRowValid(board, row)) {
return SudokuValidationResult.invalid(
"Строка " + (row + 1) + " содержит дубликаты",
"Row " + (row + 1)
);
}
}
// Проверяем столбцы
for (int col = 0; col < board.getSize(); col++) {
if (!isColumnValid(board, col)) {
return SudokuValidationResult.invalid(
"Столбец " + (col + 1) + " содержит дубликаты",
"Column " + (col + 1)
);
}
}
// Проверяем 3×3 блоки
for (int blockRow = 0; blockRow < 3; blockRow++) {
for (int blockCol = 0; blockCol < 3; blockCol++) {
if (!isBlockValid(board, blockRow, blockCol)) {
return SudokuValidationResult.invalid(
"Блок (" + (blockRow + 1) + ", " + (blockCol + 1) +
") содержит дубликаты",
"Block (" + (blockRow + 1) + ", " + (blockCol + 1) + ")"
);
}
}
}
return SudokuValidationResult.valid();
}
/**
* Проверяет строку на наличие дубликатов и значений вне диапазона 1-9
*/
private boolean isRowValid(SudokuBoard board, int row) {
Set<Integer> seen = new HashSet<>();
for (int col = 0; col < board.getSize(); col++) {
int value = board.getValue(row, col);
if (value < 1 || value > 9) {
return false; // Значение вне диапазона
}
if (!seen.add(value)) {
return false; // Дубликат
}
}
return true;
}
/**
* Проверяет столбец на наличие дубликатов
*/
private boolean isColumnValid(SudokuBoard board, int col) {
Set<Integer> seen = new HashSet<>();
for (int row = 0; row < board.getSize(); row++) {
int value = board.getValue(row, col);
if (value < 1 || value > 9) {
return false;
}
if (!seen.add(value)) {
return false;
}
}
return true;
}
/**
* Проверяет 3×3 блок на наличие дубликатов
* blockRow и blockCol — индексы блока (0-2), не ячейки
*/
private boolean isBlockValid(SudokuBoard board, int blockRow, int blockCol) {
Set<Integer> seen = new HashSet<>();
int startRow = blockRow * board.getBlockSize();
int startCol = blockCol * board.getBlockSize();
for (int row = startRow; row < startRow + board.getBlockSize(); row++) {
for (int col = startCol; col < startCol + board.getBlockSize(); col++) {
int value = board.getValue(row, col);
if (value < 1 || value > 9) {
return false;
}
if (!seen.add(value)) {
return false;
}
}
}
return true;
}
}
3. Application Service
@Service
public class SudokuService {
private final SudokuValidator validator;
public SudokuService(SudokuValidator validator) {
this.validator = validator;
}
public SudokuValidationResult checkSolution(int[][] boardArray) {
try {
SudokuBoard board = new SudokuBoard(boardArray);
return validator.validate(board);
} catch (IllegalArgumentException e) {
return SudokuValidationResult.invalid(
"Невалидный размер доски: " + e.getMessage(),
"Board"
);
}
}
}
4. REST Controller
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/v1/sudoku")
public class SudokuController {
private final SudokuService sudokuService;
public SudokuController(SudokuService sudokuService) {
this.sudokuService = sudokuService;
}
@PostMapping("/validate")
public ResponseEntity<SudokuValidationResponse> validateSudoku(
@RequestBody SudokuValidationRequest request) {
SudokuValidationResult result = sudokuService.checkSolution(request.getBoard());
return ResponseEntity.ok(new SudokuValidationResponse(
result.isValid(),
result.getMessage(),
result.getErrorLocation()
));
}
}
class SudokuValidationRequest {
private int[][] board;
public int[][] getBoard() {
return board;
}
public void setBoard(int[][] board) {
this.board = board;
}
}
class SudokuValidationResponse {
private boolean valid;
private String message;
private String errorLocation;
public SudokuValidationResponse(boolean valid, String message, String errorLocation) {
this.valid = valid;
this.message = message;
this.errorLocation = errorLocation;
}
// Getters...
}
5. Unit тесты
@SpringBootTest
public class SudokuValidatorTest {
@Autowired
private SudokuValidator validator;
@Test
public void testValidSudoku() {
int[][] validBoard = {
{5, 3, 4, 6, 7, 8, 9, 1, 2},
{6, 7, 2, 1, 9, 5, 3, 4, 8},
{1, 9, 8, 3, 4, 2, 5, 6, 7},
{8, 5, 9, 7, 6, 1, 4, 2, 3},
{4, 2, 6, 8, 5, 3, 7, 9, 1},
{7, 1, 3, 9, 2, 4, 8, 5, 6},
{9, 6, 1, 5, 3, 7, 2, 8, 4},
{2, 8, 7, 4, 1, 9, 6, 3, 5},
{3, 4, 5, 2, 8, 6, 1, 7, 9}
};
SudokuBoard board = new SudokuBoard(validBoard);
SudokuValidationResult result = validator.validate(board);
assertTrue(result.isValid());
}
@Test
public void testInvalidRow() {
int[][] invalidBoard = {
{5, 5, 4, 6, 7, 8, 9, 1, 2}, // Две пятёрки в первой строке
{6, 7, 2, 1, 9, 5, 3, 4, 8},
{1, 9, 8, 3, 4, 2, 5, 6, 7},
{8, 5, 9, 7, 6, 1, 4, 2, 3},
{4, 2, 6, 8, 5, 3, 7, 9, 1},
{7, 1, 3, 9, 2, 4, 8, 5, 6},
{9, 6, 1, 5, 3, 7, 2, 8, 4},
{2, 8, 7, 4, 1, 9, 6, 3, 5},
{3, 4, 5, 2, 8, 6, 1, 7, 9}
};
SudokuBoard board = new SudokuBoard(invalidBoard);
SudokuValidationResult result = validator.validate(board);
assertFalse(result.isValid());
assertTrue(result.getMessage().contains("Строка"));
}
@Test
public void testInvalidBlock() {
int[][] invalidBoard = {
{5, 3, 4, 6, 7, 8, 9, 1, 2},
{6, 7, 2, 1, 9, 5, 3, 4, 8},
{1, 9, 5, 3, 4, 2, 5, 6, 7}, // Две пятёрки в левом верхнем блоке
{8, 5, 9, 7, 6, 1, 4, 2, 3},
{4, 2, 6, 8, 5, 3, 7, 9, 1},
{7, 1, 3, 9, 2, 4, 8, 5, 6},
{9, 6, 1, 5, 3, 7, 2, 8, 4},
{2, 8, 7, 4, 1, 9, 6, 3, 5},
{3, 4, 5, 2, 8, 6, 1, 7, 9}
};
SudokuBoard board = new SudokuBoard(invalidBoard);
SudokuValidationResult result = validator.validate(board);
assertFalse(result.isValid());
}
}
Пример использования
curl -X POST http://localhost:8080/api/v1/sudoku/validate \
-H "Content-Type: application/json" \
-d '{
"board": [
[5,3,4,6,7,8,9,1,2],
[6,7,2,1,9,5,3,4,8],
[1,9,8,3,4,2,5,6,7],
[8,5,9,7,6,1,4,2,3],
[4,2,6,8,5,3,7,9,1],
[7,1,3,9,2,4,8,5,6],
[9,6,1,5,3,7,2,8,4],
[2,8,7,4,1,9,6,3,5],
[3,4,5,2,8,6,1,7,9]
]
}'
Временная и пространственная сложность
- Time Complexity: O(1) — всегда проверяем ровно 9×9 = 81 ячейку
- Space Complexity: O(1) — используем Set максимум на 9 элементов, всегда O(9) = O(1)
Этот подход чистый, тестируемый и соответствует Clean Architecture.