grid 4.1 → 5.0
raw patch · 5 files changed
+416/−96 lines, 5 filesdep ~QuickCheckPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: QuickCheck
API changes (from Hackage documentation)
+ Math.Geometry.Grid: data RectOctGrid
+ Math.Geometry.Grid: data TorOctGrid
+ Math.Geometry.Grid: data UnboundedOctGrid
+ Math.Geometry.Grid: rectOctGrid :: Int -> Int -> RectOctGrid
+ Math.Geometry.Grid: torOctGrid :: Int -> Int -> TorOctGrid
+ Math.Geometry.GridInternal: data RectOctGrid
+ Math.Geometry.GridInternal: data TorOctGrid
+ Math.Geometry.GridInternal: data UnboundedOctGrid
+ Math.Geometry.GridInternal: instance BoundedGrid RectOctGrid
+ Math.Geometry.GridInternal: instance Eq RectOctGrid
+ Math.Geometry.GridInternal: instance Eq TorOctGrid
+ Math.Geometry.GridInternal: instance FiniteGrid RectOctGrid
+ Math.Geometry.GridInternal: instance FiniteGrid TorOctGrid
+ Math.Geometry.GridInternal: instance Grid RectOctGrid
+ Math.Geometry.GridInternal: instance Grid TorOctGrid
+ Math.Geometry.GridInternal: instance Grid UnboundedOctGrid
+ Math.Geometry.GridInternal: instance Show RectOctGrid
+ Math.Geometry.GridInternal: instance Show TorOctGrid
+ Math.Geometry.GridInternal: instance Show UnboundedOctGrid
+ Math.Geometry.GridInternal: instance WrappedGrid TorOctGrid
+ Math.Geometry.GridInternal: rectOctGrid :: Int -> Int -> RectOctGrid
+ Math.Geometry.GridInternal: torOctGrid :: Int -> Int -> TorOctGrid
- Math.Geometry.GridMap: map :: (GridMap gm v, GridMap gm b) => (v -> b) -> gm v -> gm b
+ Math.Geometry.GridMap: map :: (GridMap gm v, GridMap gm v2, Index (BaseGrid gm v) ~ Index (BaseGrid gm v2)) => (v -> v2) -> gm v -> gm v2
- Math.Geometry.GridMap: mapWithKey :: (GridMap gm v, k ~ Index (BaseGrid gm v), GridMap gm v2) => (k -> v -> v2) -> gm v -> gm v2
+ Math.Geometry.GridMap: mapWithKey :: (GridMap gm v, k ~ Index (BaseGrid gm v), k ~ Index (BaseGrid gm v2), GridMap gm v2) => (k -> v -> v2) -> gm v -> gm v2
Files
- grid.cabal +2/−2
- src/Math/Geometry/Grid.hs +41/−5
- src/Math/Geometry/GridInternal.hs +118/−3
- src/Math/Geometry/GridMap.hs +7/−3
- test/Math/Geometry/GridQC.hs +248/−83
grid.cabal view
@@ -1,5 +1,5 @@ name: grid-version: 4.1+version: 5.0 synopsis: Tools for working with regular grids (graphs, lattices). description: Provides tools for working with regular arrangements of tiles, such as might be used in a board game or some@@ -42,7 +42,7 @@ build-depends: base ==4.*, exact-combinatorics ==0.2.*, test-framework-quickcheck2 ==0.3.*,- QuickCheck ==2.4.* || ==2.5.*,+ QuickCheck ==2.4.* || ==2.5.* || ==2.6.*, test-framework ==0.8.*, grid, base-unicode-symbols ==0.2.*
src/Math/Geometry/Grid.hs view
@@ -12,9 +12,34 @@ -- The userguide is available at -- <https://github.com/mhwombat/grid/wiki>. --+-- In this package, tiles are called \"triangular\", \"square\", etc.,+-- based on the number of /neighbours/ they have.+-- For example, a square tile has four neighbours, and a hexagonal+-- tile has six.+-- There are only three regular polygons that can tile a plane:+-- triangles, squares, and hexagons.+-- Of course, other plane tilings are possible if you use irregular +-- polygons, or curved shapes, or if you combine tiles of different +-- shapes.+--+-- When you tile other surfaces, things get very interesting.+-- Octagons will tile a /hyperbolic/ plane.+-- (Alternatively, you can think of these as squares on a board game+-- where diagonal moves are allowed.)+--+-- For a board game, you probably want to choose the grid type based+-- on the number of /directions/ a player can move, rather than the+-- number of sides the tile will have when you display it.+-- For example, for a game that uses square tiles and allows the user+-- to move diagonally as well as horizontally and vertically,+-- consider using one of the grids with /octagonal/ tiles to model the+-- board.+-- You can still /display/ the tiles as squares, but for internal+-- calculations they are octagons.+-- -- NOTE: Version 4.0 uses associated (type) synonyms instead of -- multi-parameter type classes.-+-- -- NOTE: Version 3.0 changed the order of parameters for many functions. -- This makes it easier for the user to write mapping and folding -- operations.@@ -25,6 +50,8 @@ module Math.Geometry.Grid (+ -- * Example+ -- $Example -- * The Grid class Grid(..), FiniteGrid(..),@@ -61,9 +88,16 @@ hexHexGrid, -- ** Parallelogram-shaped grid with hexagonal tiles ParaHexGrid,- paraHexGrid- -- * Example- -- $Example+ paraHexGrid,+ -- * Grids with octagonal tiles+ -- ** Unbounded grid with octagonal tiles+ UnboundedOctGrid,+ -- ** Rectangular grid with octagonal tiles+ RectOctGrid,+ rectOctGrid,+ -- ** Toroidal grid with octagonal tiles+ TorOctGrid,+ torOctGrid ) where import Math.Geometry.GridInternal (Grid(..), FiniteGrid(..), @@ -71,7 +105,9 @@ ParaTriGrid, paraTriGrid, RectTriGrid, rectTriGrid, TorTriGrid, torTriGrid, UnboundedSquareGrid, RectSquareGrid, rectSquareGrid, TorSquareGrid, torSquareGrid, - UnboundedHexGrid, HexHexGrid, hexHexGrid, ParaHexGrid, paraHexGrid)+ UnboundedHexGrid, HexHexGrid, hexHexGrid, ParaHexGrid, paraHexGrid, + UnboundedOctGrid, RectOctGrid, rectOctGrid, TorOctGrid, torOctGrid)+ {- $Example Create a grid.
src/Math/Geometry/GridInternal.hs view
@@ -37,7 +37,12 @@ HexHexGrid, hexHexGrid, ParaHexGrid,- paraHexGrid+ paraHexGrid,+ UnboundedOctGrid,+ RectOctGrid,+ rectOctGrid,+ TorOctGrid,+ torOctGrid, ) where import Prelude hiding (null)@@ -49,7 +54,7 @@ import Data.Ord.Unicode ((≤), (≥)) -- | A regular arrangement of tiles.--- Minimal complete definition: @indices@ and @distance@.+-- Minimal complete definition: @Index@, @indices@ and @distance@. class Grid g where type Index g @@ -225,6 +230,9 @@ -- Triangular tiles -- +-- | An unbounded grid with triangular tiles.+-- The grid and its indexing scheme are illustrated in the user guide,+-- available at <https://github.com/mhwombat/grid/wiki>. data UnboundedTriGrid = UnboundedTriGrid deriving Show instance Grid UnboundedTriGrid where@@ -453,6 +461,9 @@ -- Square tiles -- +-- | An unbounde grid with square tiles.+-- The grid and its indexing scheme are illustrated in the user guide,+-- available at <https://github.com/mhwombat/grid/wiki>. data UnboundedSquareGrid = UnboundedSquareGrid deriving Show instance Grid UnboundedSquareGrid where@@ -540,7 +551,7 @@ -- [((x-1) `mod` c,y), (x,(y+1) `mod` r), ((x+1) `mod` c,y), -- (x,(y-1) `mod` r)] neighbours g = nub . map (normalise g) . neighbours UnboundedSquareGrid- distance g@(TorSquareGrid (r,c) _) (x1, y1) (x2, y2) =+ distance g@(TorSquareGrid (r,c) _) (x1, y1) (x2, y2) = -- TODO redo if g `contains` (x1, y1) && g `contains` (x2, y2) then min adx (abs (c-adx)) + min ady (abs (r-ady)) else undefined @@ -555,6 +566,18 @@ normalise g (x,y) = (x `mod` c, y `mod` r) where (r, c) = size g +denormaliseTor+ :: (FiniteGrid g, Index g ~ (Int, Int), (Int, Int) ~ Size g) =>+ g -> Index g -> [Index g]+denormaliseTor g (x,y) = nub [(x2,y1), (x,y1), (x1,y1), + (x2,y), (x,y), (x1,y),+ (x2,y2), (x,y2), (x1,y2)]+ where (r, c) = size g+ x1 = x + c+ y1 = y + r+ x2 = x - c+ y2 = y - r+ -- | @'torSquareGrid' r c@ returns a toroidal grid with @r@ -- rows and @c@ columns, using square tiles. If @r@ and @c@ are -- both nonnegative, the resulting grid will have @r*c@ tiles. Otherwise, @@ -566,6 +589,9 @@ -- Hexagonal tiles -- +-- | An unbounded grid with hexagonal tiles+-- The grid and its indexing scheme are illustrated in the user guide,+-- available at <https://github.com/mhwombat/grid/wiki>. data UnboundedHexGrid = UnboundedHexGrid deriving Show instance Grid UnboundedHexGrid where@@ -656,4 +682,93 @@ paraHexGrid ∷ Int → Int → ParaHexGrid paraHexGrid r c = ParaHexGrid (r,c) [(x, y) | x ← [0..c-1], y ← [0..r-1]]+++--+-- Octagonal tiles+--++-- | An unbounded grid with octagonal tiles.+-- The grid and its indexing scheme are illustrated in the user guide,+-- available at <https://github.com/mhwombat/grid/wiki>.+data UnboundedOctGrid = UnboundedOctGrid deriving Show++instance Grid UnboundedOctGrid where+ type Index UnboundedOctGrid = (Int, Int)+ indices _ = undefined+ neighbours _ (x,y) = [(x-1,y+1), (x,y+1), (x+1,y+1), (x+1,y), + (x+1,y-1), (x,y-1), (x-1,y-1), (x-1,y)]+ distance _ (x1, y1) (x2, y2) = max (abs (x2-x1)) (abs (y2-y1))+ contains _ _ = True++--+-- Rectangular grids with octagonal tiles+--++-- | A rectangular grid with octagonal tiles.+-- The grid and its indexing scheme are illustrated in the user guide,+-- available at <https://github.com/mhwombat/grid/wiki>.+data RectOctGrid = RectOctGrid (Int, Int) [(Int, Int)] deriving Eq++instance Show RectOctGrid where + show (RectOctGrid (r,c) _) = + "rectOctGrid " ++ show r ++ " " ++ show c++instance Grid RectOctGrid where+ type Index RectOctGrid = (Int, Int)+ indices (RectOctGrid _ xs) = xs+ neighbours = neighboursBasedOn UnboundedOctGrid+ distance = distanceBasedOn UnboundedOctGrid++instance FiniteGrid RectOctGrid where+ type Size RectOctGrid = (Int, Int)+ size (RectOctGrid s _) = s++instance BoundedGrid RectOctGrid where+ tileSideCount _ = 4+ boundary g = cartesianIndices . size $ g+ centre g = cartesianCentre . size $ g++-- | @'rectOctGrid' r c@ produces a rectangular grid with @r@ rows+-- and @c@ columns, using octagonal tiles. If @r@ and @c@ are both +-- nonnegative, the resulting grid will have @r*c@ tiles. Otherwise, +-- the resulting grid will be null and the list of indices will be +-- null.+rectOctGrid ∷ Int → Int → RectOctGrid+rectOctGrid r c = + RectOctGrid (r,c) [(x,y) | x ← [0..c-1], y ← [0..r-1]]++--+-- Toroidal grids with octagonal tiles.+--++-- | A toroidal grid with octagonal tiles.+-- The grid and its indexing scheme are illustrated in the user guide,+-- available at <https://github.com/mhwombat/grid/wiki>.+data TorOctGrid = TorOctGrid (Int, Int) [(Int, Int)] deriving Eq++instance Show TorOctGrid where + show (TorOctGrid (r,c) _) = "torOctGrid " ++ show r ++ " " ++ show c++instance Grid TorOctGrid where+ type Index TorOctGrid = (Int, Int)+ indices (TorOctGrid _ xs) = xs+ neighbours g = nub . map (normalise g) . neighbours UnboundedOctGrid+ distance g a b = minimum . map (distance UnboundedOctGrid a) $ bs+ where bs = denormaliseTor g b++instance FiniteGrid TorOctGrid where+ type Size TorOctGrid = (Int, Int)+ size (TorOctGrid s _) = s++instance WrappedGrid TorOctGrid where+ normalise g (x,y) = (x `mod` c, y `mod` r)+ where (r, c) = size g++-- | @'torOctGrid' r c@ returns a toroidal grid with @r@ +-- rows and @c@ columns, using octagonal tiles. If @r@ and @c@ are +-- both nonnegative, the resulting grid will have @r*c@ tiles. Otherwise, +-- the resulting grid will be null and the list of indices will be null.+torOctGrid ∷ Int → Int → TorOctGrid+torOctGrid r c = TorOctGrid (r,c) [(x, y) | x ← [0..c-1], y ← [0..r-1]]
src/Math/Geometry/GridMap.hs view
@@ -42,7 +42,7 @@ -- | A regular arrangement of tiles, having a value associated with -- each tile. -- Minimal complete definition: @toMap@, @toGrid@, @adjustWithKey@,--- @mapWithKey.+-- @mapWithKey@. -- -- Note: Some of the methods have an @Ord@ constraint on the grid -- index. This is purely to make it easier to write implementations.@@ -101,12 +101,16 @@ elems = M.elems . toMap -- | Map a function over all values in the map.- map ∷ GridMap gm b ⇒ (v → b) → gm v → gm b+ map + ∷ (GridMap gm v2, + G.Index (BaseGrid gm v) ~ G.Index (BaseGrid gm v2)) ⇒ + (v → v2) → gm v → gm v2 map f = mapWithKey (\_ v → f v) -- | Map a function over all values in the map. mapWithKey - ∷ (k ~ G.Index (BaseGrid gm v), GridMap gm v2) ⇒ + ∷ (k ~ G.Index (BaseGrid gm v), k ~ G.Index (BaseGrid gm v2), + GridMap gm v2) ⇒ (k → v → v2) → gm v → gm v2 {- $Compare
test/Math/Geometry/GridQC.hs view
@@ -31,6 +31,17 @@ pointAt g i = indices g !! (i `mod` n) where n = (length . indices) g +minPathCount+ ∷ (Eq (Index g), Grid g) ⇒ g → Index g → Index g → Int+minPathCount g a b = length . minimalPaths g a $ b++minPathCount2+ ∷ (Eq (Index g), Grid g) ⇒ g → [Index g] → Index g → Int+minPathCount2 g as b = sum . map (\x → minPathCount g x b) $ as++neighbourCount ∷ ∀ g. Grid g ⇒ g → Index g → Int+neighbourCount g x = length . neighbours g $ x+ -- -- Tests that should apply to and are identical for all grids --@@ -61,10 +72,10 @@ sort (delete a (neighbours g a)) ≡ sort expected where a = g `pointAt` i expected = map fst $ filter (\p → 1 ≡ snd p) $ viewpoint g a--- Note: In a small grid, a tile can be its own neighbour. However, when--- we calculate the distance between a tile and itself, we get 0, not 1.--- That's why we have to delete the tile from its list before comparing --- to the result from the neighbours function.+-- Note: In a small but unbounded grid, a tile can be its own neighbour.+-- However, when we calculate the distance between a tile and itself, we+-- get 0, not 1. That's why we have to delete the tile from its list +-- before comparing to the result from the neighbours function. prop_edges_cw_neighbours ∷ (Grid g, Ord (Index g)) ⇒ g → Int → Property prop_edges_cw_neighbours g i = nonNull g ==> @@ -182,9 +193,7 @@ prop_TriTriGrid_neighbour_count_in_bounds ∷ TriTriGrid → Int → Property prop_TriTriGrid_neighbour_count_in_bounds g i = nonNull g ==>- if tileCount g ≡ 1- then length (neighbours g x) ≡ 0- else length (neighbours g x) `elem` [1,2,3]+ neighbourCount g x ≤ 3 where x = g `pointAt` i prop_TriTriGrid_boundary_count_correct ∷ TriTriGrid → Property@@ -237,9 +246,7 @@ prop_ParaTriGrid_neighbour_count_in_bounds ∷ ParaTriGrid → Int → Property prop_ParaTriGrid_neighbour_count_in_bounds g i = nonNull g ==>- if tileCount g ≡ 1- then length (neighbours g x) ≡ 0- else length (neighbours g x) `elem` [1,2,3]+ neighbourCount g x ≤ 3 where x = g `pointAt` i prop_ParaTriGrid_boundary_count_correct ∷ ParaTriGrid → Property@@ -283,9 +290,7 @@ prop_RectTriGrid_neighbour_count_in_bounds ∷ RectTriGrid → Int → Property prop_RectTriGrid_neighbour_count_in_bounds g i = nonNull g ==>- if tileCount g ≡ 1- then length (neighbours g x) ≡ 0- else length (neighbours g x) `elem` [1,2,3]+ neighbourCount g x ≤ 3 where x = g `pointAt` i prop_RectTriGrid_boundary_count_correct ∷ RectTriGrid → Property@@ -330,9 +335,7 @@ prop_TorTriGrid_neighbour_count_in_bounds ∷ TorTriGrid → Int → Property prop_TorTriGrid_neighbour_count_in_bounds g i = nonNull g ==>- if tileCount g ≡ 1- then length (neighbours g x) ≡ 0- else length (neighbours g x) `elem` [1,2,3]+ neighbourCount g x ≤ 3 where x = g `pointAt` i --@@ -374,18 +377,14 @@ prop_RectSquareGrid_neighbour_count_in_bounds ∷ RectSquareGrid → Int → Property-prop_RectSquareGrid_neighbour_count_in_bounds g i = nonNull g ==> f+prop_RectSquareGrid_neighbour_count_in_bounds g i = nonNull g ==> + neighbourCount g x ≤ 4 where x = g `pointAt` i- neighbourCount = length (neighbours g x)- (r, c) = size g- f | tileCount g ≡ 1 = neighbourCount ≡ 0- | r ≡ 1 || c ≡ 1 = neighbourCount `elem` [1,2]- | otherwise = neighbourCount `elem` [2,3,4] prop_RectSquareGrid_num_min_paths_correct ∷ RectSquareGrid → Int → Int → Property prop_RectSquareGrid_num_min_paths_correct g i j = nonNull g ==>- length (minimalPaths g a b) ≡ M.choose (deltaX+deltaY) deltaX+ minPathCount g a b ≡ M.choose (deltaX+deltaY) deltaX where a = g `pointAt` i b = g `pointAt` j deltaX = abs $ fst b - fst a@@ -408,7 +407,7 @@ ----- Toroidal grids with square-ish tiles+-- Toroidal grids with square tiles -- -- We want the number of tiles in a test grid to be O(n)@@ -442,21 +441,14 @@ ps = indices g a = head ps b = last ps- f | r ≡ 1 && c ≡ 1 = 0 -- zero-size torus+ f | r ≡ 1 && c ≡ 1 = 0 -- single-tile torus | r ≡ 1 || c ≡ 1 = 1 -- a and b are the same | otherwise = 2 prop_TorSquareGrid_neighbour_count_in_bounds ∷ TorSquareGrid → Int → Property-prop_TorSquareGrid_neighbour_count_in_bounds g i = nonNull g ==> f+prop_TorSquareGrid_neighbour_count_in_bounds g i = nonNull g ==>+ neighbourCount g x ≤ 4 where x = g `pointAt` i- neighbourCount = length . neighbours g $ x- (r, c) = size g- f | tileCount g ≡ 1 = neighbourCount ≡ 1- | (r,c) ≡ (1,2) || (r,c) ≡ (2,1) = neighbourCount ≡ 2- | (r,c) ≡ (2,2) = neighbourCount ≡ 2- | r ≡ 1 || c ≡ 1 = neighbourCount ≡ 3- | r ≡ 2 || c ≡ 2 = neighbourCount ≡ 3- | otherwise = neighbourCount ≡ 4 -- -- Circular hexagonal grids @@ -494,9 +486,7 @@ prop_HexHexGrid_neighbour_count_in_bounds ∷ HexHexGrid → Int → Property prop_HexHexGrid_neighbour_count_in_bounds g i = nonNull g ==> - if tileCount g ≡ 1- then length (neighbours g x) ≡ 0- else length (neighbours g x) `elem` [2,3,4,5,6]+ neighbourCount g x ≤ 6 where x = g `pointAt` i prop_HexHexGrid_boundary_count_correct ∷ HexHexGrid → Property@@ -549,13 +539,9 @@ (r, c) = size g prop_ParaHexGrid_neighbour_count_in_bounds ∷ ParaHexGrid → Int → Property-prop_ParaHexGrid_neighbour_count_in_bounds g i = nonNull g ==> f+prop_ParaHexGrid_neighbour_count_in_bounds g i = nonNull g ==>+ neighbourCount g x ≤ 6 where x = g `pointAt` i- neighbourCount = length (neighbours g x)- (r, c) = size g- f | tileCount g ≡ 1 = neighbourCount ≡ 0- | r ≡ 1 || c ≡ 1 = neighbourCount `elem` [1,2]- | otherwise = neighbourCount `elem` [2,3,4,5,6] prop_ParaHexGrid_boundary_count_correct ∷ ParaHexGrid → Property prop_ParaHexGrid_boundary_count_correct g = property $@@ -565,7 +551,110 @@ prop_ParaHexGrid_boundary_tiles_have_fewer_neighbours g = property $ all (5>) . map (numNeighbours g) . boundary $ g +--+-- Rectangular grids with octagonal tiles+-- +-- We want the number of tiles in a test grid to be O(n)+sizedRectOctGrid ∷ Int → Gen RectOctGrid+sizedRectOctGrid n = do+ let n' = min n 12 -- calculation time for these grids grows quickly!+ r ← choose (0,n')+ let c = n' `div` (r+1)+ return $ rectOctGrid r c++instance Arbitrary RectOctGrid where+ arbitrary = sized sizedRectOctGrid++prop_RectOctGrid_tile_count_correct ∷ RectOctGrid → Property+prop_RectOctGrid_tile_count_correct g = property $ + tileCount g ≡ if r ≤ 0 || c ≤ 0 then 0 else r*c+ where (r, c) = size g++prop_RectOctGrid_distance_in_bounds ∷ RectOctGrid → Int → Int → Property+prop_RectOctGrid_distance_in_bounds g i j = nonNull g ==>+ distance g a b ≤ max r c+ where (r, c) = size g+ a = g `pointAt` i+ b = g `pointAt` j++-- If the ordering produced by rectOctGrid is ever changed, this+-- property may need to be changed too. It relies on the first and last +-- elements being at opposite corners.+prop_RectOctGrid_distance_corner_to_corner ∷ RectOctGrid → Property+prop_RectOctGrid_distance_corner_to_corner g = r > 0 && c > 0 ==> + a ≡ b || distance g a b ≡ (max r c) - 1+ where (r, c) = size g+ ps = indices g+ a = head ps+ b = last ps++prop_RectOctGrid_neighbour_count_in_bounds ∷ + RectOctGrid → Int → Property+prop_RectOctGrid_neighbour_count_in_bounds g i = nonNull g ==>+ neighbourCount g x ≤ 8+ where x = g `pointAt` i++prop_RectOctGrid_num_min_paths_correct ∷ + RectOctGrid → Int → Int → Property+prop_RectOctGrid_num_min_paths_correct g i j = nonNull g ==>+ minPathCount g a b ≡+ if a ≡ b then 1 else minPathCount2 g att b+ where a = g `pointAt` i+ b = g `pointAt` j+ att = adjacentTilesToward g a b++prop_RectOctGrid_boundary_count_correct ∷ RectOctGrid → Property+prop_RectOctGrid_boundary_count_correct g = property $+ (length . boundary) g ≡ (cartesianBoundaryCount . size) g++prop_RectOctGrid_boundary_tiles_have_fewer_neighbours ∷ RectOctGrid → Property+prop_RectOctGrid_boundary_tiles_have_fewer_neighbours g = property $+ all (6>) . map (numNeighbours g) . boundary $ g+++--+-- Toroidal grids with octagonal tiles+--++-- We want the number of tiles in a test grid to be O(n)+sizedTorOctGrid ∷ Int → Gen TorOctGrid+sizedTorOctGrid n = do+ r ← choose (0,n)+ let c = n `div` (r+1)+ return $ torOctGrid r c++instance Arbitrary TorOctGrid where+ arbitrary = sized sizedTorOctGrid++prop_TorOctGrid_tile_count_correct ∷ TorOctGrid → Property+prop_TorOctGrid_tile_count_correct g = property $ + tileCount g ≡ if r ≤ 0 || c ≤ 0 then 0 else r*c+ where (r, c) = size g++prop_TorOctGrid_distance_in_bounds ∷ TorOctGrid → Int → Int → Property+prop_TorOctGrid_distance_in_bounds g i j = nonNull g ==>+ distance g a b ≤ min r c + abs (r-c)+ where (r, c) = size g+ a = g `pointAt` i+ b = g `pointAt` j++-- If the ordering produced by torOctGrid is ever changed, this property+-- may need to be changed too.+prop_TorOctGrid_distance_corner_to_corner ∷ TorOctGrid → Property+prop_TorOctGrid_distance_corner_to_corner g = r > 0 && c > 0 ==> + distance g a b ≡ if tileCount g ≡ 1 then 0 else 1+ where (r, c) = size g+ ps = indices g+ a = head ps+ b = last ps++prop_TorOctGrid_neighbour_count_in_bounds ∷ TorOctGrid → Int → Property+prop_TorOctGrid_neighbour_count_in_bounds g i = nonNull g ==>+ neighbourCount g x ≤ 8+ where x = g `pointAt` i++ test ∷ Test test = testGroup "Math.Geometry.GridQC" [@@ -601,17 +690,17 @@ testProperty "prop_neighbours_cw_viewpoint - TriTriGrid" (prop_neighbours_cw_viewpoint ∷ TriTriGrid → Int → Property), testProperty "prop_edges_cw_neighbours - TriTriGrid"- ( prop_edges_cw_neighbours ∷ TriTriGrid → Int → Property),+ (prop_edges_cw_neighbours ∷ TriTriGrid → Int → Property), testProperty "prop_edges_are_adjacent - TriTriGrid"- ( prop_edges_are_adjacent ∷ TriTriGrid → Property),+ (prop_edges_are_adjacent ∷ TriTriGrid → Property), testProperty "prop_adjacentTilesToward_moves_closer - TriTriGrid"- ( prop_adjacentTilesToward_moves_closer ∷ + (prop_adjacentTilesToward_moves_closer ∷ TriTriGrid → Int → Int → Property), testProperty "prop_minimal_paths_have_min_length - TriTriGrid"- ( prop_minimal_paths_have_min_length ∷ + (prop_minimal_paths_have_min_length ∷ TriTriGrid → Int → Int → Property), testProperty "prop_minimal_paths_are_valid - TriTriGrid"- ( prop_minimal_paths_are_valid ∷ TriTriGrid → Int → Int → Property),+ (prop_minimal_paths_are_valid ∷ TriTriGrid → Int → Int → Property), -- ParaTriGrid tests testProperty "prop_ParaTriGrid_tile_count_correct"@@ -645,17 +734,17 @@ testProperty "prop_neighbours_cw_viewpoint - ParaTriGrid" (prop_neighbours_cw_viewpoint ∷ ParaTriGrid → Int → Property), testProperty "prop_edges_cw_neighbours - ParaTriGrid"- ( prop_edges_cw_neighbours ∷ ParaTriGrid → Int → Property),+ (prop_edges_cw_neighbours ∷ ParaTriGrid → Int → Property), testProperty "prop_edges_are_adjacent - ParaTriGrid"- ( prop_edges_are_adjacent ∷ ParaTriGrid → Property),+ (prop_edges_are_adjacent ∷ ParaTriGrid → Property), testProperty "prop_adjacentTilesToward_moves_closer - ParaTriGrid"- ( prop_adjacentTilesToward_moves_closer ∷ + (prop_adjacentTilesToward_moves_closer ∷ ParaTriGrid → Int → Int → Property), testProperty "prop_minimal_paths_have_min_length - ParaTriGrid"- ( prop_minimal_paths_have_min_length ∷ + (prop_minimal_paths_have_min_length ∷ ParaTriGrid → Int → Int → Property), testProperty "prop_minimal_paths_are_valid - ParaTriGrid"- ( prop_minimal_paths_are_valid ∷ ParaTriGrid → Int → Int → Property),+ (prop_minimal_paths_are_valid ∷ ParaTriGrid → Int → Int → Property), -- RectTriGrid tests testProperty "prop_RectTriGrid_tile_count_correct"@@ -687,17 +776,17 @@ testProperty "prop_neighbours_cw_viewpoint - RectTriGrid" (prop_neighbours_cw_viewpoint ∷ RectTriGrid → Int → Property), testProperty "prop_edges_cw_neighbours - RectTriGrid"- ( prop_edges_cw_neighbours ∷ RectTriGrid → Int → Property),+ (prop_edges_cw_neighbours ∷ RectTriGrid → Int → Property), testProperty "prop_edges_are_adjacent - RectTriGrid"- ( prop_edges_are_adjacent ∷ RectTriGrid → Property),+ (prop_edges_are_adjacent ∷ RectTriGrid → Property), testProperty "prop_adjacentTilesToward_moves_closer - RectTriGrid"- ( prop_adjacentTilesToward_moves_closer ∷ + (prop_adjacentTilesToward_moves_closer ∷ RectTriGrid → Int → Int → Property), testProperty "prop_minimal_paths_have_min_length - RectTriGrid"- ( prop_minimal_paths_have_min_length ∷ + (prop_minimal_paths_have_min_length ∷ RectTriGrid → Int → Int → Property), testProperty "prop_minimal_paths_are_valid - RectTriGrid"- ( prop_minimal_paths_are_valid ∷ RectTriGrid → Int → Int → Property),+ (prop_minimal_paths_are_valid ∷ RectTriGrid → Int → Int → Property), -- TorTriGrid tests testProperty "prop_TorTriGrid_tile_count_correct"@@ -715,17 +804,17 @@ testProperty "prop_neighbours_cw_viewpoint - TorTriGrid" (prop_neighbours_cw_viewpoint ∷ TorTriGrid → Int → Property), testProperty "prop_edges_cw_neighbours - TorTriGrid"- ( prop_edges_cw_neighbours ∷ TorTriGrid → Int → Property),+ (prop_edges_cw_neighbours ∷ TorTriGrid → Int → Property), testProperty "prop_edges_are_adjacent - TorTriGrid"- ( prop_edges_are_adjacent ∷ TorTriGrid → Property),+ (prop_edges_are_adjacent ∷ TorTriGrid → Property), testProperty "prop_adjacentTilesToward_moves_closer - TorTriGrid"- ( prop_adjacentTilesToward_moves_closer ∷ + (prop_adjacentTilesToward_moves_closer ∷ TorTriGrid → Int → Int → Property), testProperty "prop_minimal_paths_have_min_length - TorTriGrid"- ( prop_minimal_paths_have_min_length ∷ + (prop_minimal_paths_have_min_length ∷ TorTriGrid → Int → Int → Property), testProperty "prop_minimal_paths_are_valid - TorTriGrid"- ( prop_minimal_paths_are_valid ∷ TorTriGrid → Int → Int → Property),+ (prop_minimal_paths_are_valid ∷ TorTriGrid → Int → Int → Property), -- RectSquareGrid tests testProperty "prop_RectSquareGrid_tile_count_correct"@@ -759,17 +848,17 @@ testProperty "prop_neighbours_cw_viewpoint - RectSquareGrid" (prop_neighbours_cw_viewpoint ∷ RectSquareGrid → Int → Property), testProperty "prop_edges_cw_neighbours - RectSquareGrid"- ( prop_edges_cw_neighbours ∷ RectSquareGrid → Int → Property),+ (prop_edges_cw_neighbours ∷ RectSquareGrid → Int → Property), testProperty "prop_edges_are_adjacent - RectSquareGrid"- ( prop_edges_are_adjacent ∷ RectSquareGrid → Property),+ (prop_edges_are_adjacent ∷ RectSquareGrid → Property), testProperty "prop_adjacentTilesToward_moves_closer - RectSquareGrid"- ( prop_adjacentTilesToward_moves_closer ∷ + (prop_adjacentTilesToward_moves_closer ∷ RectSquareGrid → Int → Int → Property), testProperty "prop_minimal_paths_have_min_length - RectSquareGrid"- ( prop_minimal_paths_have_min_length ∷ + (prop_minimal_paths_have_min_length ∷ RectSquareGrid → Int → Int → Property), testProperty "prop_minimal_paths_are_valid - RectSquareGrid"- ( prop_minimal_paths_are_valid ∷ RectSquareGrid → Int → Int → Property),+ (prop_minimal_paths_are_valid ∷ RectSquareGrid → Int → Int → Property), testProperty "prop_RectSquareGrid_num_min_paths_correct" prop_RectSquareGrid_num_min_paths_correct, @@ -791,17 +880,17 @@ testProperty "prop_neighbours_cw_viewpoint - TorSquareGrid" (prop_neighbours_cw_viewpoint ∷ TorSquareGrid → Int → Property), testProperty "prop_edges_cw_neighbours - TorSquareGrid"- ( prop_edges_cw_neighbours ∷ TorSquareGrid → Int → Property),+ (prop_edges_cw_neighbours ∷ TorSquareGrid → Int → Property), testProperty "prop_edges_are_adjacent - TorSquareGrid"- ( prop_edges_are_adjacent ∷ TorSquareGrid → Property),+ (prop_edges_are_adjacent ∷ TorSquareGrid → Property), testProperty "prop_adjacentTilesToward_moves_closer - TorSquareGrid"- ( prop_adjacentTilesToward_moves_closer ∷ + (prop_adjacentTilesToward_moves_closer ∷ TorSquareGrid → Int → Int → Property), testProperty "prop_minimal_paths_have_min_length - TorSquareGrid"- ( prop_minimal_paths_have_min_length ∷ + (prop_minimal_paths_have_min_length ∷ TorSquareGrid → Int → Int → Property), testProperty "prop_minimal_paths_are_valid - TorSquareGrid"- ( prop_minimal_paths_are_valid ∷ TorSquareGrid → Int → Int → Property),+ (prop_minimal_paths_are_valid ∷ TorSquareGrid → Int → Int → Property), -- HexHexGrid tests testProperty "prop_HexHexGrid_tile_count_correct"@@ -835,17 +924,17 @@ testProperty "prop_neighbours_cw_viewpoint - HexHexGrid" (prop_neighbours_cw_viewpoint ∷ HexHexGrid → Int → Property), testProperty "prop_edges_cw_neighbours - HexHexGrid"- ( prop_edges_cw_neighbours ∷ HexHexGrid → Int → Property),+ (prop_edges_cw_neighbours ∷ HexHexGrid → Int → Property), testProperty "prop_edges_are_adjacent - HexHexGrid"- ( prop_edges_are_adjacent ∷ HexHexGrid → Property),+ (prop_edges_are_adjacent ∷ HexHexGrid → Property), testProperty "prop_adjacentTilesToward_moves_closer - HexHexGrid"- ( prop_adjacentTilesToward_moves_closer ∷ + (prop_adjacentTilesToward_moves_closer ∷ HexHexGrid → Int → Int → Property), testProperty "prop_minimal_paths_have_min_length - HexHexGrid"- ( prop_minimal_paths_have_min_length ∷ + (prop_minimal_paths_have_min_length ∷ HexHexGrid → Int → Int → Property), testProperty "prop_minimal_paths_are_valid - HexHexGrid"- ( prop_minimal_paths_are_valid ∷ HexHexGrid → Int → Int → Property),+ (prop_minimal_paths_are_valid ∷ HexHexGrid → Int → Int → Property), -- ParaHexGrid tests testProperty "prop_ParaHexGrid_tile_count_correct"@@ -879,16 +968,92 @@ testProperty "prop_neighbours_cw_viewpoint - ParaHexGrid" (prop_neighbours_cw_viewpoint ∷ ParaHexGrid → Int → Property), testProperty "prop_edges_cw_neighbours - ParaHexGrid"- ( prop_edges_cw_neighbours ∷ ParaHexGrid → Int → Property),+ (prop_edges_cw_neighbours ∷ ParaHexGrid → Int → Property), testProperty "prop_adjacentTilesToward_moves_closer - ParaHexGrid"- ( prop_adjacentTilesToward_moves_closer ∷ + (prop_adjacentTilesToward_moves_closer ∷ ParaHexGrid → Int → Int → Property), testProperty "prop_edges_are_adjacent - ParaHexGrid"- ( prop_edges_are_adjacent ∷ ParaHexGrid → Property),+ (prop_edges_are_adjacent ∷ ParaHexGrid → Property), testProperty "prop_minimal_paths_have_min_length - ParaHexGrid"- ( prop_minimal_paths_have_min_length ∷ + (prop_minimal_paths_have_min_length ∷ ParaHexGrid → Int → Int → Property), testProperty "prop_minimal_paths_are_valid - ParaHexGrid"- ( prop_minimal_paths_are_valid ∷ ParaHexGrid → Int → Int → Property)+ (prop_minimal_paths_are_valid ∷ ParaHexGrid → Int → Int → Property),++ -- RectOctGrid tests+ testProperty "prop_RectOctGrid_tile_count_correct"+ prop_RectOctGrid_tile_count_correct,+ testProperty "prop_distance_reflexive - RectOctGrid"+ (prop_distance_reflexive ∷ RectOctGrid → Int → Property),+ testProperty "prop_distance_symmetric - RectOctGrid"+ (prop_distance_symmetric ∷ RectOctGrid → Int → Int → Property),+ testProperty "prop_minDistance_cw_distance - RectOctGrid"+ (prop_minDistance_cw_distance ∷ RectOctGrid → Int → [Int] → Property),+ testProperty "prop_grid_and_boundary_are_both_null_or_not - RectOctGrid"+ (prop_grid_and_boundary_are_both_null_or_not ∷ RectOctGrid → Property),+ testProperty "prop_boundary_in_grid - RectOctGrid"+ (prop_boundary_in_grid ∷ RectOctGrid → Property),+ testProperty "prop_boundary_tiles_have_fewer_neighbours - RectOctGrid"+ (prop_boundary_tiles_have_fewer_neighbours ∷ RectOctGrid → Int → Property),+ testProperty "prop_RectOctGrid_boundary_count_correct"+ prop_RectOctGrid_boundary_count_correct,+ testProperty "prop_RectOctGrid_boundary_tiles_have_fewer_neighbours"+ prop_RectOctGrid_boundary_tiles_have_fewer_neighbours,+ testProperty "prop_centres_equidistant_from_boundary - RectOctGrid"+ (prop_centres_equidistant_from_boundary ∷ RectOctGrid → Property),+ testProperty "prop_centres_farthest_from_boundary - RectOctGrid"+ (prop_centres_farthest_from_boundary ∷ RectOctGrid → Int → Property),+ testProperty "prop_RectOctGrid_distance_in_bounds"+ prop_RectOctGrid_distance_in_bounds,+ testProperty "prop_RectOctGrid_distance_corner_to_corner"+ prop_RectOctGrid_distance_corner_to_corner,+ testProperty "prop_RectOctGrid_neighbour_count_in_bounds"+ prop_RectOctGrid_neighbour_count_in_bounds,+ testProperty "prop_neighbours_cw_viewpoint - RectOctGrid"+ (prop_neighbours_cw_viewpoint ∷ RectOctGrid → Int → Property),+ testProperty "prop_edges_cw_neighbours - RectOctGrid"+ (prop_edges_cw_neighbours ∷ RectOctGrid → Int → Property),+ testProperty "prop_edges_are_adjacent - RectOctGrid"+ (prop_edges_are_adjacent ∷ RectOctGrid → Property),+ testProperty "prop_adjacentTilesToward_moves_closer - RectOctGrid"+ (prop_adjacentTilesToward_moves_closer ∷ + RectOctGrid → Int → Int → Property),+ testProperty "prop_minimal_paths_have_min_length - RectOctGrid"+ (prop_minimal_paths_have_min_length ∷ + RectOctGrid → Int → Int → Property),+ testProperty "prop_minimal_paths_are_valid - RectOctGrid"+ (prop_minimal_paths_are_valid ∷ RectOctGrid → Int → Int → Property),+ testProperty "prop_RectOctGrid_num_min_paths_correct"+ prop_RectOctGrid_num_min_paths_correct,++ -- TorOctGrid tests+ testProperty "prop_TorOctGrid_tile_count_correct"+ prop_TorOctGrid_tile_count_correct,+ testProperty "prop_distance_reflexive - TorOctGrid"+ (prop_distance_reflexive ∷ TorOctGrid → Int → Property),+ testProperty "prop_distance_symmetric - TorOctGrid"+ (prop_distance_symmetric ∷ TorOctGrid → Int → Int → Property),+ testProperty "prop_minDistance_cw_distance - TorOctGrid"+ (prop_minDistance_cw_distance ∷ TorOctGrid → Int → [Int] → Property),+ testProperty "prop_TorOctGrid_distance_in_bounds"+ prop_TorOctGrid_distance_in_bounds,+ testProperty "prop_TorOctGrid_distance_corner_to_corner"+ prop_TorOctGrid_distance_corner_to_corner,+ testProperty "prop_TorOctGrid_neighbour_count_in_bounds"+ prop_TorOctGrid_neighbour_count_in_bounds,+ testProperty "prop_neighbours_cw_viewpoint - TorOctGrid"+ (prop_neighbours_cw_viewpoint ∷ TorOctGrid → Int → Property),+ testProperty "prop_edges_cw_neighbours - TorOctGrid"+ (prop_edges_cw_neighbours ∷ TorOctGrid → Int → Property),+ testProperty "prop_edges_are_adjacent - TorOctGrid"+ (prop_edges_are_adjacent ∷ TorOctGrid → Property),+ testProperty "prop_adjacentTilesToward_moves_closer - TorOctGrid"+ (prop_adjacentTilesToward_moves_closer ∷ + TorOctGrid → Int → Int → Property),+ testProperty "prop_minimal_paths_have_min_length - TorOctGrid"+ (prop_minimal_paths_have_min_length ∷ + TorOctGrid → Int → Int → Property),+ testProperty "prop_minimal_paths_are_valid - TorOctGrid"+ (prop_minimal_paths_are_valid ∷ TorOctGrid → Int → Int → Property) ]