working ios game
This commit is contained in:
152
ios/Shikaku/Core/ShikakuModels.swift
Normal file
152
ios/Shikaku/Core/ShikakuModels.swift
Normal file
@@ -0,0 +1,152 @@
|
||||
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..<size).contains(cell.row), (0..<size).contains(cell.col) else {
|
||||
return ValidationResult(isSolved: false, message: "A rectangle extends outside the grid")
|
||||
}
|
||||
|
||||
if coverage[cell] != nil {
|
||||
return ValidationResult(isSolved: false, message: "Cells overlap between two rectangles")
|
||||
}
|
||||
|
||||
coverage[cell] = index
|
||||
}
|
||||
}
|
||||
|
||||
let totalCells = size * size
|
||||
if coverage.count != totalCells {
|
||||
return ValidationResult(isSolved: false, message: "Not all cells are covered (\(coverage.count)/\(totalCells))")
|
||||
}
|
||||
|
||||
for rect in playerRects {
|
||||
let clueValues = rect.cells.compactMap { clues[$0] }
|
||||
|
||||
if clueValues.isEmpty {
|
||||
return ValidationResult(isSolved: false, message: "A rectangle contains no clue number")
|
||||
}
|
||||
|
||||
if clueValues.count > 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!")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user