Skip to content
This repository
Fetching contributors…

Cannot retrieve contributors at this time

file 104 lines (96 sloc) 3.42 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104
{-# LANGUAGE OverloadedStrings #-}
module Roguestar.Lib.Facing
    (Facing(..),
     facingToRelative,
     facingToRelative7,
     stringToFacing,
     facingDistance,
     isFacing,
     faceAt)
    where

import Roguestar.Lib.Position
import Data.Ord
import Data.List
import qualified Data.ByteString.Char8 as B

data Facing = North
| NorthEast
| East
| SouthEast
| South
| SouthWest
| West
| NorthWest
| Here
deriving (Eq,Ord,Enum,Bounded,Read,Show)

-- |
-- Takes an abbreviation (n,e,sw, etc) and answers a facing.
-- The input string must be lower case.
-- No form of "Here" is an acceptable input to this function.
--
stringToFacing :: B.ByteString -> Maybe Facing
stringToFacing "n" = Just North
stringToFacing "ne" = Just NorthEast
stringToFacing "e" = Just East
stringToFacing "se" = Just SouthEast
stringToFacing "s" = Just South
stringToFacing "sw" = Just SouthWest
stringToFacing "w" = Just West
stringToFacing "nw" = Just NorthWest
stringToFacing _ = Nothing

-- |
-- In relative coordinates, one integral step in the specified direction.
--
facingToRelative :: Facing -> (Integer,Integer)
facingToRelative North = (0,1)
facingToRelative NorthEast = (1,1)
facingToRelative East = (1,0)
facingToRelative SouthEast = (1,-1)
facingToRelative South = (0,-1)
facingToRelative SouthWest = (-1,-1)
facingToRelative West = (-1,0)
facingToRelative NorthWest = (-1,1)
facingToRelative Here = (0,0)

-- |
-- In relative coordinates, roughly seven integral steps in the specified direction.
--
facingToRelative7 :: Facing -> (Integer,Integer)
facingToRelative7 North = (0,7)
facingToRelative7 NorthEast = (5,5)
facingToRelative7 East = (7,0)
facingToRelative7 SouthEast = (5,-5)
facingToRelative7 South = (0,-7)
facingToRelative7 SouthWest = (-5,-5)
facingToRelative7 West = (-7,0)
facingToRelative7 NorthWest = (-5,5)
facingToRelative7 Here = (0,0)

-- |
-- The distance between two facings, between 0 and 4.
--
facingDistance :: Facing -> Facing -> Integer
facingDistance Here _ = 0
facingDistance _ Here = 0
facingDistance a b = toInteger $ if enum_distance > 4 then 8 - enum_distance else enum_distance
    where enum_distance = abs $ fromEnum a - fromEnum b

-- |
-- A test function to detect when one Position + Facing points directly at another Position.
--
isFacing :: (PositionType a,PositionType b) => (a, Facing) -> b -> Bool
isFacing (as,face) bs = or $ map (\(a,b) -> f face (fromPosition a) (fromPosition b)) $ positionPairs as bs
    where f :: Facing -> (Integer,Integer) -> (Integer,Integer) -> Bool
          f North (x,y) (u,v) = x == u && v >= y
          f NorthEast (x,y) (u,v) = x - u == y - v && u >= x
          f East (x,y) (u,v) = y == v && u >= x
          f SouthEast (x,y) (u,v) = x - u == v - y && u >= x
          f South (x,y) (u,v) = x == u && v <= y
          f SouthWest (x,y) (u,v) = x - u == y - v && u <= x
          f West (x,y) (u,v) = y == v && u <= x
          f NorthWest (x,y) (u,v) = x - y == v - y && u <= x
          f Here xy uv = xy == uv

-- |
-- Which facing most closely points from the first Position to the second.
--
faceAt :: Position -> Position -> Facing
faceAt here there = fst $ minimumBy (comparing snd) $
    map (\x -> let face = Position $ facingToRelative7 x in
                      (x,distanceBetweenSquared there face -
                         distanceBetweenSquared here face)) [minBound..maxBound]
Something went wrong with that request. Please try again.