import Foundation struct GridPoint: Hashable, Sendable { let row: Int let col: Int } struct CellBounds: Hashable, Sendable { let startRow: Int let startCol: Int let endRow: Int let endCol: Int var height: Int { endRow - startRow + 1 } var width: Int { endCol - startCol + 1 } var area: Int { height * width } func contains(_ point: GridPoint) -> Bool { startRow <= point.row && point.row <= endRow && startCol <= point.col && point.col <= endCol } var cells: [GridPoint] { var result: [GridPoint] = [] result.reserveCapacity(area) for row in startRow...endRow { for col in startCol...endCol { result.append(GridPoint(row: row, col: col)) } } return result } } struct SolutionRect: Hashable, Sendable { let row: Int let col: Int let height: Int let width: Int var area: Int { height * width } var bounds: CellBounds { CellBounds(startRow: row, startCol: col, endRow: row + height - 1, endCol: col + width - 1) } var cells: [GridPoint] { bounds.cells } } struct PlayerRect: Identifiable, Hashable, Sendable { let id: UUID let startRow: Int let startCol: Int let endRow: Int let endCol: Int init(id: UUID = UUID(), startRow: Int, startCol: Int, endRow: Int, endCol: Int) { self.id = id self.startRow = startRow self.startCol = startCol self.endRow = endRow self.endCol = endCol } var bounds: CellBounds { CellBounds( startRow: min(startRow, endRow), startCol: min(startCol, endCol), endRow: max(startRow, endRow), endCol: max(startCol, endCol) ) } var area: Int { bounds.area } var cells: [GridPoint] { bounds.cells } func contains(_ point: GridPoint) -> Bool { bounds.contains(point) } } struct ShikakuPuzzle: Sendable { let size: Int let clues: [GridPoint: Int] let solution: [SolutionRect] let seed: UInt64 } struct ValidationResult: Equatable, Sendable { let isSolved: Bool let message: String } enum ShikakuValidation { static func verify(size: Int, playerRects: [PlayerRect], clues: [GridPoint: Int]) -> ValidationResult { var coverage: [GridPoint: Int] = [:] for (index, rect) in playerRects.enumerated() { for cell in rect.cells { guard (0.. 1 { return ValidationResult(isSolved: false, message: "A rectangle contains more than one clue number") } let clueValue = clueValues[0] if rect.area != clueValue { return ValidationResult(isSolved: false, message: "A rectangle contains \(clueValue) but has \(rect.area) cells") } } return ValidationResult(isSolved: true, message: "Solved!") } }