2020-11-08 19:18:42 +01:00
|
|
|
{-# LANGUAGE LambdaCase #-}
|
|
|
|
|
|
|
|
module Internal.World
|
2020-11-15 13:07:08 +01:00
|
|
|
( World(..),
|
2020-11-08 19:18:42 +01:00
|
|
|
worldWithDimensions,
|
|
|
|
getBeepersAt,
|
|
|
|
setBeepersAt,
|
|
|
|
hasWallAt,
|
|
|
|
addWallAt,
|
|
|
|
removeWallAt,
|
|
|
|
positionWithinWorld,
|
|
|
|
)
|
|
|
|
where
|
|
|
|
|
|
|
|
import qualified Data.Map.Strict as M
|
|
|
|
import qualified Data.Set as S
|
|
|
|
import Internal.Types
|
|
|
|
|
|
|
|
data World = World
|
|
|
|
{ worldDimensions :: Vector,
|
|
|
|
worldWalls :: M.Map Vector (S.Set Direction),
|
|
|
|
worldBeepers :: M.Map Vector Int
|
|
|
|
}
|
|
|
|
deriving (Show, Eq)
|
|
|
|
|
|
|
|
worldWithDimensions :: Vector -> World
|
|
|
|
worldWithDimensions (width, height)
|
|
|
|
| width <= 0 || height <= 0 = error "Cannot have world with zero or negative dimension"
|
|
|
|
| otherwise =
|
|
|
|
World
|
|
|
|
{ worldDimensions = (width, height),
|
|
|
|
worldWalls = M.empty,
|
|
|
|
worldBeepers = M.empty
|
|
|
|
}
|
|
|
|
|
|
|
|
positionWithinWorld :: World -> Vector -> Bool
|
|
|
|
positionWithinWorld w (x, y) = valid_x && valid_y
|
|
|
|
where
|
|
|
|
(world_width, world_height) = worldDimensions w
|
|
|
|
valid_x = x > 0 && x <= world_width
|
|
|
|
valid_y = y > 0 && y <= world_height
|
|
|
|
|
|
|
|
setBeepersAt :: Vector -> Int -> World -> World
|
|
|
|
setBeepersAt pos count w
|
|
|
|
| not $ positionWithinWorld w pos = error "Invalid position"
|
|
|
|
| count < 0 = error "Invalid count of worldBeepers"
|
|
|
|
| count == 0 = w {worldBeepers = M.delete pos old_beepers}
|
|
|
|
| otherwise = w {worldBeepers = M.insert pos count old_beepers}
|
|
|
|
where
|
|
|
|
old_beepers = worldBeepers w
|
|
|
|
|
|
|
|
getBeepersAt :: Vector -> World -> Int
|
|
|
|
getBeepersAt pos w
|
|
|
|
| not $ positionWithinWorld w pos = error "Invalid position"
|
|
|
|
| otherwise = M.findWithDefault 0 pos (worldBeepers w)
|
|
|
|
|
|
|
|
switchDirectionToInternal :: Vector -> Direction -> (Vector, Direction)
|
|
|
|
switchDirectionToInternal (x, y) = \case
|
|
|
|
West -> ((x - 1, y), East)
|
|
|
|
South -> ((x, y - 1), North)
|
|
|
|
dir -> ((x, y), dir)
|
|
|
|
|
|
|
|
isOnEdge :: Vector -> Vector -> Direction -> Bool
|
|
|
|
isOnEdge (width, height) (x, y) dir = horizontally || vertically
|
|
|
|
where
|
|
|
|
horizontally = x == width && dir == East
|
|
|
|
vertically = y == height && dir == North
|
|
|
|
|
|
|
|
addWallAt :: Vector -> Direction -> World -> World
|
|
|
|
addWallAt pos dir w
|
|
|
|
| not $ positionWithinWorld w newPosition = error "Invalid position"
|
|
|
|
| isOnEdge (worldDimensions w) newPosition newDirection = w
|
2020-11-15 13:07:08 +01:00
|
|
|
| otherwise = w {worldWalls = M.insert newPosition newWalls (worldWalls w)}
|
2020-11-08 19:18:42 +01:00
|
|
|
where
|
|
|
|
(newPosition, newDirection) = switchDirectionToInternal pos dir
|
2020-11-15 13:07:08 +01:00
|
|
|
oldWalls = M.findWithDefault S.empty newPosition (worldWalls w)
|
|
|
|
newWalls = S.union oldWalls $ S.singleton newDirection
|
2020-11-08 19:18:42 +01:00
|
|
|
|
|
|
|
removeWallAt :: Vector -> Direction -> World -> World
|
|
|
|
removeWallAt pos dir w
|
|
|
|
| not $ positionWithinWorld w newPosition = error "Invalid position"
|
|
|
|
| isOnEdge (worldDimensions w) newPosition newDirection = w
|
2020-11-15 13:07:08 +01:00
|
|
|
| S.null newWalls = w {worldWalls = M.delete newPosition (worldWalls w)}
|
|
|
|
| otherwise = w {worldWalls = M.insert newPosition newWalls (worldWalls w)}
|
2020-11-08 19:18:42 +01:00
|
|
|
where
|
|
|
|
(newPosition, newDirection) = switchDirectionToInternal pos dir
|
2020-11-15 13:07:08 +01:00
|
|
|
oldWalls = M.findWithDefault S.empty newPosition (worldWalls w)
|
|
|
|
newWalls = oldWalls S.\\ S.singleton newDirection
|
2020-11-08 19:18:42 +01:00
|
|
|
|
|
|
|
hasWallAt :: Vector -> Direction -> World -> Bool
|
|
|
|
hasWallAt pos dir w
|
|
|
|
| not $ positionWithinWorld w newPosition = False
|
|
|
|
| isOnEdge (worldDimensions w) newPosition newDirection = True
|
2020-11-15 13:07:08 +01:00
|
|
|
| otherwise = case worldWalls w M.!? newPosition of
|
|
|
|
Just wallsInPlace -> S.member newDirection wallsInPlace
|
2020-11-08 19:18:42 +01:00
|
|
|
Nothing -> False
|
|
|
|
where
|
|
|
|
(newPosition, newDirection) = switchDirectionToInternal pos dir
|