refactor(xan): Switch to V2 over tuples most places

These are generally rather nicer to work due to some typeclass instances,
and integrate better with other ecosystems for things like linear
algebra etc.

Change-Id: I546c8da7b17234648f3d612b28741c1fded25447
Reviewed-on: https://cl.tvl.fyi/c/depot/+/910
Tested-by: BuildkiteCI
Reviewed-by: glittershark <grfn@gws.fyi>
This commit is contained in:
Griffin Smith 2020-07-03 20:32:36 -04:00 committed by glittershark
parent 4455f28e42
commit 9b8d3185fe
12 changed files with 172 additions and 143 deletions

View file

@ -19,6 +19,7 @@ import Xanthous.Util (between)
import Xanthous.Util.Optparse
import Xanthous.Data (Dimensions, width, height)
import Xanthous.Generators.Util
import Linear.V2
--------------------------------------------------------------------------------
data Params = Params
@ -102,7 +103,7 @@ generate' params dims = do
stepAutomata :: forall s g. MCells s -> Dimensions -> Params -> CellM g s ()
stepAutomata cells dims params = do
origCells <- lift $ cloneMArray @_ @(STUArray s) cells
for_ (range ((0, 0), (dims ^. width, dims ^. height))) $ \pos -> do
for_ (range (0, V2 (dims ^. width) (dims ^. height))) $ \pos -> do
neighs <- lift $ numAliveNeighborsM origCells pos
origValue <- lift $ readArray origCells pos
lift . writeArray cells pos

View file

@ -159,14 +159,14 @@ fillRoom cells room =
V2 dimx dimy = room ^. dimensions
in for_ [posx .. posx + dimx] $ \x ->
for_ [posy .. posy + dimy] $ \y ->
lift $ writeArray cells (x, y) True
lift $ writeArray cells (V2 x y) True
corridorBetween :: MonadRandom m => Room -> Room -> m [(Word, Word)]
corridorBetween :: MonadRandom m => Room -> Room -> m [V2 Word]
corridorBetween originRoom destinationRoom
= straightLine <$> origin <*> destination
where
origin = choose . NE.fromList . map toTuple =<< originEdge
destination = choose . NE.fromList . map toTuple =<< destinationEdge
origin = choose . NE.fromList =<< originEdge
destination = choose . NE.fromList =<< destinationEdge
originEdge = pickEdge originRoom originCorner
destinationEdge = pickEdge destinationRoom destinationCorner
pickEdge room corner = choose . over both (boxEdge room) $ cornerEdges corner
@ -188,4 +188,3 @@ corridorBetween originRoom destinationRoom
(EQ, EQ) -> TopLeft -- should never happen
destinationCorner = opposite originCorner
toTuple (V2 x y) = (x, y)

View file

@ -14,13 +14,15 @@ import Control.Monad.Random
import Data.Array.IArray (amap, bounds, rangeSize, (!))
import qualified Data.Array.IArray as Arr
import Data.Foldable (any, toList)
import Linear.V2
--------------------------------------------------------------------------------
import Xanthous.Generators.Util
import Xanthous.Random
import Xanthous.Data ( Position, _Position, positionFromPair
, rotations, arrayNeighbors, Neighbors(..)
, neighborPositions
)
import Xanthous.Data
( positionFromV2, Position, _Position
, rotations, arrayNeighbors, Neighbors(..)
, neighborPositions
)
import Xanthous.Data.EntityMap (EntityMap, _EntityMap)
import Xanthous.Entities.Raws (rawsWithType, RawType)
import qualified Xanthous.Entities.Item as Item
@ -49,7 +51,7 @@ randomDoors cells = do
doorRatio <- getRandomR subsetRange
let numDoors = floor $ doorRatio * fromIntegral (length candidateCells)
doorPositions =
removeAdjacent . fmap positionFromPair . take numDoors $ candidateCells
removeAdjacent . fmap positionFromV2 . take numDoors $ candidateCells
doors = zip doorPositions $ repeat unlockedDoor
pure $ _EntityMap # doors
where
@ -92,8 +94,9 @@ tutorialMessage cells characterPosition = do
accessiblePositionsWithin :: Int -> Cells -> Position -> [Position]
accessiblePositionsWithin dist valid pos =
review _Position
<$> filter (\(px, py) -> not $ valid ! (fromIntegral px, fromIntegral py))
(circle (pos ^. _Position) dist)
<$> filter
(\pt -> not $ valid ! (fromIntegral <$> pt))
(circle (pos ^. _Position) dist)
randomEntities
:: forall entity raw m. (MonadRandom m, RawType raw)
@ -116,10 +119,10 @@ randomEntities newWithType sizeRange cells =
pure $ _EntityMap # entities
randomPosition :: MonadRandom m => Cells -> m Position
randomPosition = fmap positionFromPair . choose . impureNonNull . cellCandidates
randomPosition = fmap positionFromV2 . choose . impureNonNull . cellCandidates
-- cellCandidates :: Cells -> Cells
cellCandidates :: Cells -> Set (Word, Word)
cellCandidates :: Cells -> Set (V2 Word)
cellCandidates
-- find the largest contiguous region of cells in the cave.
= maximumBy (compare `on` length)

View file

@ -20,6 +20,7 @@ module Xanthous.Generators.Util
) where
--------------------------------------------------------------------------------
import Xanthous.Prelude hiding (Foldable, toList, for_)
--------------------------------------------------------------------------------
import Data.Array.ST
import Data.Array.Unboxed
import Control.Monad.ST
@ -28,13 +29,14 @@ import Data.Monoid
import Data.Foldable (Foldable, toList, for_)
import qualified Data.Set as Set
import Data.Semigroup.Foldable
import Linear.V2
--------------------------------------------------------------------------------
import Xanthous.Util (foldlMapM', maximum1, minimum1)
import Xanthous.Data (Dimensions, width, height)
--------------------------------------------------------------------------------
type MCells s = STUArray s (Word, Word) Bool
type Cells = UArray (Word, Word) Bool
type MCells s = STUArray s (V2 Word) Bool
type Cells = UArray (V2 Word) Bool
type CellM g s a = RandT g (ST s) a
randInitialize :: RandomGen g => Dimensions -> Double -> CellM g s (MCells s)
@ -43,28 +45,28 @@ randInitialize dims aliveChance = do
for_ [0..dims ^. width] $ \i ->
for_ [0..dims ^. height] $ \j -> do
val <- (>= aliveChance) <$> getRandomR (0, 1)
lift $ writeArray res (i, j) val
lift $ writeArray res (V2 i j) val
pure res
initializeEmpty :: RandomGen g => Dimensions -> CellM g s (MCells s)
initializeEmpty dims =
lift $ newArray ((0, 0), (dims ^. width, dims ^. height)) False
lift $ newArray (0, V2 (dims ^. width) (dims ^. height)) False
numAliveNeighborsM
:: forall a i j m
. (MArray a Bool m, Ix (i, j), Integral i, Integral j)
=> a (i, j) Bool
-> (i, j)
:: forall a i m
. (MArray a Bool m, Ix i, Integral i)
=> a (V2 i) Bool
-> V2 i
-> m Word
numAliveNeighborsM cells (x, y) = do
numAliveNeighborsM cells (V2 x y) = do
cellBounds <- getBounds cells
getSum <$> foldlMapM'
(fmap (Sum . fromIntegral . fromEnum) . boundedGet cellBounds)
neighborPositions
where
boundedGet :: ((i, j), (i, j)) -> (Int, Int) -> m Bool
boundedGet ((minX, minY), (maxX, maxY)) (i, j)
boundedGet :: (V2 i, V2 i) -> (Int, Int) -> m Bool
boundedGet (V2 minX minY, V2 maxX maxY) (i, j)
| x <= minX
|| y <= minY
|| x >= maxX
@ -73,23 +75,23 @@ numAliveNeighborsM cells (x, y) = do
| otherwise =
let nx = fromIntegral $ fromIntegral x + i
ny = fromIntegral $ fromIntegral y + j
in readArray cells (nx, ny)
in readArray cells $ V2 nx ny
numAliveNeighbors
:: forall a i j
. (IArray a Bool, Ix (i, j), Integral i, Integral j)
=> a (i, j) Bool
-> (i, j)
:: forall a i
. (IArray a Bool, Ix i, Integral i)
=> a (V2 i) Bool
-> V2 i
-> Word
numAliveNeighbors cells (x, y) =
numAliveNeighbors cells (V2 x y) =
let cellBounds = bounds cells
in getSum $ foldMap
(Sum . fromIntegral . fromEnum . boundedGet cellBounds)
neighborPositions
where
boundedGet :: ((i, j), (i, j)) -> (Int, Int) -> Bool
boundedGet ((minX, minY), (maxX, maxY)) (i, j)
boundedGet :: (V2 i, V2 i) -> (Int, Int) -> Bool
boundedGet (V2 minX minY, V2 maxX maxY) (i, j)
| x <= minX
|| y <= minY
|| x >= maxX
@ -98,20 +100,20 @@ numAliveNeighbors cells (x, y) =
| otherwise =
let nx = fromIntegral $ fromIntegral x + i
ny = fromIntegral $ fromIntegral y + j
in cells ! (nx, ny)
in cells ! V2 nx ny
neighborPositions :: [(Int, Int)]
neighborPositions = [(i, j) | i <- [-1..1], j <- [-1..1], (i, j) /= (0, 0)]
fillOuterEdgesM :: (MArray a Bool m, Ix i, Ix j) => a (i, j) Bool -> m ()
fillOuterEdgesM :: (MArray a Bool m, Ix i) => a (V2 i) Bool -> m ()
fillOuterEdgesM arr = do
((minX, minY), (maxX, maxY)) <- getBounds arr
(V2 minX minY, V2 maxX maxY) <- getBounds arr
for_ (range (minX, maxX)) $ \x -> do
writeArray arr (x, minY) True
writeArray arr (x, maxY) True
writeArray arr (V2 x minY) True
writeArray arr (V2 x maxY) True
for_ (range (minY, maxY)) $ \y -> do
writeArray arr (minX, y) True
writeArray arr (maxX, y) True
writeArray arr (V2 minX y) True
writeArray arr (V2 maxX y) True
cloneMArray
:: forall a a' i e m.
@ -128,20 +130,20 @@ cloneMArray = thaw @_ @UArray <=< freeze
-- | Flood fill a cell array starting at a point, returning a list of all the
-- (true) cell locations reachable from that point
floodFill :: forall a i j.
floodFill :: forall a i.
( IArray a Bool
, Ix (i, j)
, Enum i , Enum j
, Bounded i , Bounded j
, Eq i , Eq j
, Ix i
, Enum i
, Bounded i
, Eq i
)
=> a (i, j) Bool -- ^ array
-> (i, j) -- ^ position
-> Set (i, j)
=> a (V2 i) Bool -- ^ array
-> (V2 i) -- ^ position
-> Set (V2 i)
floodFill = go mempty
where
go :: Set (i, j) -> a (i, j) Bool -> (i, j) -> Set (i, j)
go res arr@(bounds -> arrBounds) idx@(x, y)
go :: Set (V2 i) -> a (V2 i) Bool -> (V2 i) -> Set (V2 i)
go res arr@(bounds -> arrBounds) idx@(V2 x y)
| not (inRange arrBounds idx) = res
| not (arr ! idx) = res
| otherwise =
@ -149,7 +151,7 @@ floodFill = go mempty
= filter (inRange arrBounds)
. filter (/= idx)
. filter (`notMember` res)
$ (,)
$ V2
<$> [(if x == minBound then x else pred x)
..
(if x == maxBound then x else succ x)]
@ -162,19 +164,19 @@ floodFill = go mempty
in r' `seq` go r' arr idx')
else r)
(res & contains idx .~ True) neighbors
{-# SPECIALIZE floodFill :: UArray (Word, Word) Bool -> (Word, Word) -> Set (Word, Word) #-}
{-# SPECIALIZE floodFill :: UArray (V2 Word) Bool -> (V2 Word) -> Set (V2 Word) #-}
-- | Gives a list of all the disconnected regions in a cell array, represented
-- each as lists of points
regions :: forall a i j.
regions :: forall a i.
( IArray a Bool
, Ix (i, j)
, Enum i , Enum j
, Bounded i , Bounded j
, Eq i , Eq j
, Ix i
, Enum i
, Bounded i
, Eq i
)
=> a (i, j) Bool
-> [Set (i, j)]
=> a (V2 i) Bool
-> [Set (V2 i)]
regions arr
| Just firstPoint <- findFirstPoint arr =
let region = floodFill arr firstPoint
@ -182,9 +184,9 @@ regions arr
in region : regions arr'
| otherwise = []
where
findFirstPoint :: a (i, j) Bool -> Maybe (i, j)
findFirstPoint :: a (V2 i) Bool -> Maybe (V2 i)
findFirstPoint = fmap fst . headMay . filter snd . assocs
{-# SPECIALIZE regions :: UArray (Word, Word) Bool -> [Set (Word, Word)] #-}
{-# SPECIALIZE regions :: UArray (V2 Word) Bool -> [Set (V2 Word)] #-}
fillAll :: (IArray a Bool, Ix i, Foldable f) => f i -> a i Bool -> a i Bool
fillAll ixes a = accum (const fst) a $ (, (False, ())) <$> toList ixes

View file

@ -73,9 +73,9 @@ fromCave' wallPositions = failing (pure ()) $ do
where
insertEntity e pt = modify $ EntityMap.insertAt (ptToPos pt) $ SomeEntity e
ptToPos pt = _Position # (pt & both %~ fromIntegral)
ptToPos pt = _Position # (fromIntegral <$> pt)
stepOut :: Set (Word, Word) -> [[(Word, Word)]] -> MaybeT m [[(Word, Word)]]
stepOut :: Set (V2 Word) -> [[V2 Word]] -> MaybeT m [[V2 Word]]
stepOut circ rooms = for rooms $ \room ->
let nextLevels = hashNub $ toList . neighborCells =<< room
in pure