module Position
(Position(..),
MultiPosition,
multiPosition,
PositionType(..),
distanceBetweenSquared,
distanceBetweenChessboard,
positionPairs)
where
import Data.List
import qualified Data.Set as Set
-- | Position of an object in \"chessboard space\".
newtype Position = Position { fromPosition :: (Integer,Integer) }
deriving (Eq,Ord,Read,Show)
-- | For objects, such as buildings, that occupy multiple positions.
newtype MultiPosition = MultiPosition { fromMultiPosition :: [Position] }
instance Eq MultiPosition where
(==) (MultiPosition as) (MultiPosition bs) = Set.fromList as == Set.fromList bs
instance Ord MultiPosition where
compare (MultiPosition as) (MultiPosition bs) = Set.fromList as `compare` Set.fromList bs
class PositionType p where
toMultiPosition :: p -> MultiPosition
offsetPosition :: (Integer,Integer) -> p -> p
instance PositionType Position where
toMultiPosition p = MultiPosition [p]
offsetPosition (x,y) (Position (u,v)) = Position (x+u,y+v)
instance PositionType MultiPosition where
toMultiPosition = id
offsetPosition xy (MultiPosition ps) = MultiPosition $ map (offsetPosition xy) ps
-- | Construct a 'MultiPosition' from a base position and a list of offsets.
-- The base position always counts as part of the MultiPosition.
multiPosition :: Position -> [(Integer,Integer)] -> MultiPosition
multiPosition (Position xy) xys = MultiPosition $ nub $ Position xy : map (offsetPosition xy . Position) xys
-- | Pythagorean distance, squared.
-- For multi-positions, measures the minimal distance.
distanceBetweenSquared :: (PositionType a,PositionType b) => a -> b -> Integer
distanceBetweenSquared as bs = minimum $
do Position (x,y) <- fromMultiPosition $ toMultiPosition as
Position (u,v) <- fromMultiPosition $ toMultiPosition bs
return $ (x - u)^2 + (y - v)^2
-- | Number of squares you would have to move (as a queen on a chessboard) to arrive from the first position to the second.
-- For multi-positions, measures the minimal distance.
distanceBetweenChessboard :: (PositionType a,PositionType b) => a -> b -> Integer
distanceBetweenChessboard as bs = minimum $
do Position (x,y) <- fromMultiPosition $ toMultiPosition as
Position (u,v) <- fromMultiPosition $ toMultiPosition bs
return $ max (abs $ u - x) (abs $ v - y)
-- | List all pairs of positions between two MutiPositions.
positionPairs :: (PositionType a,PositionType b) => a -> b -> [(Position,Position)]
positionPairs as bs =
do a <- fromMultiPosition $ toMultiPosition as
b <- fromMultiPosition $ toMultiPosition bs
return (a,b)