125 lines
4.4 KiB
Swift
125 lines
4.4 KiB
Swift
import XCTest
|
|
@testable import Shikaku
|
|
|
|
final class ShikakuCoreTests: XCTestCase {
|
|
func testGeneratedPuzzlesAreCoveredAndValidated() throws {
|
|
let cases: [(size: Int, seeds: ClosedRange<UInt64>)] = [
|
|
(5, 1...20),
|
|
(7, 1...20),
|
|
(10, 1...5),
|
|
(15, 1...5),
|
|
(20, 1...2),
|
|
(25, 1...2)
|
|
]
|
|
|
|
for testCase in cases {
|
|
for seed in testCase.seeds {
|
|
let size = testCase.size
|
|
let puzzle = try ShikakuGenerator.generate(size: size, seed: seed)
|
|
var covered: Set<GridPoint> = []
|
|
|
|
XCTAssertEqual(puzzle.size, size)
|
|
XCTAssertEqual(puzzle.clues.count, puzzle.solution.count)
|
|
|
|
for rect in puzzle.solution {
|
|
XCTAssertGreaterThanOrEqual(rect.area, 2)
|
|
|
|
for cell in rect.cells {
|
|
XCTAssertTrue((0..<size).contains(cell.row))
|
|
XCTAssertTrue((0..<size).contains(cell.col))
|
|
XCTAssertTrue(covered.insert(cell).inserted, "Cell \(cell) was covered more than once")
|
|
}
|
|
|
|
let cluesInside = rect.cells.compactMap { puzzle.clues[$0] }
|
|
XCTAssertEqual(cluesInside, [rect.area])
|
|
}
|
|
|
|
XCTAssertEqual(covered.count, size * size)
|
|
|
|
let playerRects = puzzle.solution.map {
|
|
PlayerRect(
|
|
startRow: $0.row,
|
|
startCol: $0.col,
|
|
endRow: $0.row + $0.height - 1,
|
|
endCol: $0.col + $0.width - 1
|
|
)
|
|
}
|
|
let validation = ShikakuValidation.verify(size: size, playerRects: playerRects, clues: puzzle.clues)
|
|
XCTAssertTrue(validation.isSolved)
|
|
}
|
|
}
|
|
}
|
|
|
|
func testSeededGenerationIsRepeatable() throws {
|
|
let first = try ShikakuGenerator.generate(size: 10, seed: 123_456_789)
|
|
let second = try ShikakuGenerator.generate(size: 10, seed: 123_456_789)
|
|
|
|
XCTAssertEqual(first.seed, second.seed)
|
|
XCTAssertEqual(first.clues, second.clues)
|
|
XCTAssertEqual(first.solution, second.solution)
|
|
}
|
|
|
|
func testValidationRejectsOverlappingRectangles() {
|
|
let clues = [
|
|
GridPoint(row: 0, col: 0): 2,
|
|
GridPoint(row: 1, col: 1): 2
|
|
]
|
|
let playerRects = [
|
|
PlayerRect(startRow: 0, startCol: 0, endRow: 0, endCol: 1),
|
|
PlayerRect(startRow: 0, startCol: 1, endRow: 1, endCol: 1)
|
|
]
|
|
|
|
let validation = ShikakuValidation.verify(size: 2, playerRects: playerRects, clues: clues)
|
|
|
|
XCTAssertFalse(validation.isSolved)
|
|
XCTAssertEqual(validation.message, "Cells overlap between two rectangles")
|
|
}
|
|
|
|
@MainActor
|
|
func testPlacingARectangleRemovesOverlappingPlayerRects() {
|
|
let game = ShikakuGame(size: 5)
|
|
game.placeRect(PlayerRect(startRow: 0, startCol: 0, endRow: 0, endCol: 1))
|
|
game.placeRect(PlayerRect(startRow: 0, startCol: 1, endRow: 1, endCol: 1))
|
|
|
|
XCTAssertEqual(game.playerRects.count, 1)
|
|
XCTAssertTrue(game.playerRects[0].contains(GridPoint(row: 0, col: 1)))
|
|
XCTAssertTrue(game.playerRects[0].contains(GridPoint(row: 1, col: 1)))
|
|
}
|
|
|
|
@MainActor
|
|
func testSingleCellPlacementIsRejected() {
|
|
let game = ShikakuGame(size: 5)
|
|
|
|
game.placeRect(PlayerRect(startRow: 0, startCol: 0, endRow: 0, endCol: 0))
|
|
|
|
XCTAssertTrue(game.playerRects.isEmpty)
|
|
XCTAssertEqual(game.message, "Drag over at least 2 cells")
|
|
}
|
|
|
|
@MainActor
|
|
func testGameCanStartFromRequestedSeed() {
|
|
let game = ShikakuGame(size: 5)
|
|
|
|
game.newPuzzle(seed: 987_654_321)
|
|
|
|
XCTAssertEqual(game.currentSeed, 987_654_321)
|
|
XCTAssertEqual(game.size, 5)
|
|
XCTAssertTrue(game.playerRects.isEmpty)
|
|
}
|
|
|
|
func testValidationAcceptsKnownSolution() {
|
|
let clues = [
|
|
GridPoint(row: 0, col: 0): 2,
|
|
GridPoint(row: 1, col: 0): 2
|
|
]
|
|
let playerRects = [
|
|
PlayerRect(startRow: 0, startCol: 0, endRow: 0, endCol: 1),
|
|
PlayerRect(startRow: 1, startCol: 0, endRow: 1, endCol: 1)
|
|
]
|
|
|
|
let validation = ShikakuValidation.verify(size: 2, playerRects: playerRects, clues: clues)
|
|
|
|
XCTAssertTrue(validation.isSolved)
|
|
}
|
|
}
|