Skip to content

Instantly share code, notes, and snippets.

@patrl
Created January 17, 2023 16:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save patrl/9f013f6ac3270d4fc75dfe9ef25e7df3 to your computer and use it in GitHub Desktop.
Save patrl/9f013f6ac3270d4fc75dfe9ef25e7df3 to your computer and use it in GitHub Desktop.
Recursive symmetric shadowcasting by quadrant in Haskell
computeQ :: TileMap -> Transform -> Slope -> Slope -> Int -> Visible ()
computeQ m t s e y = do
let xmin = roundTiesUp (fromIntegral y * s) -- compute the initial x coordinate bounded by the starting slope
let xmax = roundTiesDown (fromIntegral y * e) -- compute the ending x coordinate bounded by the ending slope
let xys = (coord . (,y)) <$> [xmin..xmax] -- create a list of coordinates to recurse through by pairing xs with the depth
-- a helper function to recurse through a list of coordinates, and potentially create branching row computations
-- the first argument is a Maybe Bool which tracks whether the previous coordinate was a wall
-- Nothing indicates that there was no previous coordinate (we're at the beginning of the list)
-- s indicates the starting slope; this can change as the recursion proceeds.
-- xys indicates the list of coordinates
go Nothing s xys where
-----------------------------------------
-- definition of row recursion follows --
-----------------------------------------
-- if the last tile in the row is visible, then continue onto the next row, otherwise break the recursion
go final s' [] = unless (final == Just True) $ do
computeQ m t s' e (y + 1)
-- case 1: no previous tile.
-- this indicates that we're at the beginning of a row recursion
go Nothing s' (c:cs) = do
let w = isWall m (t c) -- w is a boolean which indicates whether the current focus is a wall (and therefore invisible)
when (w || isSymmetric s' e c) $ do -- when the current tile is a wall or symmetric...
addToVis (t c) -- ...add the tranformed coordinate it to the visible coordinates
go (Just w) s' cs -- now, recurse through the remainder of the wall, setting the Maybe Bool to depend on whether the focus is a wall
-- case 2: there is a previous tile, whether it was a wall or not is indicated by w'
go (Just w') s' (c:cs) = do
let w = isWall m (t c) -- let w indicate the wallhood os te current focus
when (w || isSymmetric s' e c) $ do -- when the current tile is a wall or symmetric...
addToVis (t c) -- ...add the transformed coordinate to the visible coordinates
if -- the next action depends on wallhood of the current and previous tiles
| (w' && not w) -> -- if the previous tile is a wall, and the current tile isn't a wall...
go (Just w) (slope c) cs -- continue the recursion through the row, but adjust the start slope to brush the prev tile
| (not w' && w) -> do -- if the previous tile isn't a wall, but the current tile is a wall
computeQ m t s' (slope c) (y + 1) -- start a new branching computation on the next row, with an adjusted end slope
go (Just w) s' cs -- continue recursing through the row
| otherwise ->
go (Just w) s' cs -- in other circumstances, continue recursing through the row
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment