... introduce newtype Rank
and newtype File
.
module Game.Chess.Square (
Rank (Rank1,Rank2,Rank3,Rank4,Rank5,Rank6,Rank7,Rank8),mkRank,unRank,
File (FileA,FileB,FileC,FileD,FileE,FileF,FileG,FileH),mkFile,unFile,
Sq (A1,A2,A3,A4,A5,A6,A7,A8
,B1,B2,B3,B4,B5,B6,B7,B8
,C1,C2,C3,C4,C5,C6,C7,C8
,D1,D2,D3,D4,D5,D6,D7,D8
,E1,E2,E3,E4,E5,E6,E7,E8
,F1,F2,F3,F4,F5,F6,F7,F8
,G1,G2,G3,G4,G5,G6,G7,G8
,H1,H2,H3,H4,H5,H6,H7,H8)
, mkSq, unSq,
isDark, isLight,
IsSquare (..), toCoord, toRF, toIndex,
) where
import Game.Chess.Internal.Prelude
import Data.Char (ord, chr)
import GHC.Stack
import Data.Ix (Ix (..))
-------------------------------------------------------------------------------
-- Rank
-------------------------------------------------------------------------------
newtype Rank = Rank Int
deriving (Eq, Ord)
unRank :: Rank -> Int
unRank = coerce
-- TODO: make efficient version
mkRank :: HasCallStack => Int -> Rank
mkRank n
| n >= 0 && n <= 7 = Rank n
| otherwise = error $ "mkRank " ++ show n
instance Show Rank where
showsPrec _ (Rank 0) = showString "Rank1"
showsPrec _ (Rank 1) = showString "Rank2"
showsPrec _ (Rank 2) = showString "Rank3"
showsPrec _ (Rank 3) = showString "Rank4"
showsPrec _ (Rank 4) = showString "Rank5"
showsPrec _ (Rank 5) = showString "Rank6"
showsPrec _ (Rank 6) = showString "Rank7"
showsPrec _ (Rank 7) = showString "Rank8"
showsPrec d (Rank n) = showParen (d > 10) $
showString "Rank " . showsPrec 11 n
instance Enum Rank where
toEnum n | n >= 0 && n <= 7 = Rank n
| otherwise = error $ "Rank out-of-bound " ++ show n
fromEnum (Rank n) = n
instance Bounded Rank where
minBound = Rank1
maxBound = Rank8
pattern Rank1, Rank2, Rank3, Rank4, Rank5, Rank6, Rank7, Rank8 :: Rank
pattern Rank1 = Rank 0
pattern Rank2 = Rank 1
pattern Rank3 = Rank 2
pattern Rank4 = Rank 3
pattern Rank5 = Rank 4
pattern Rank6 = Rank 5
pattern Rank7 = Rank 6
pattern Rank8 = Rank 7
{-# COMPLETE Rank1, Rank2, Rank3, Rank4, Rank5, Rank6, Rank7, Rank8 :: Rank #-}
-------------------------------------------------------------------------------
-- File
-------------------------------------------------------------------------------
newtype File = File Int
deriving (Eq, Ord)
unFile :: File -> Int
unFile = coerce
mkFile :: HasCallStack => Int -> File
mkFile n
| n >= 0 && n <= 7 = File n
| otherwise = error $ "mkFile " ++ show n
instance Show File where
showsPrec _ (File 0) = showString "FileA"
showsPrec _ (File 1) = showString "FileB"
showsPrec _ (File 2) = showString "FileC"
showsPrec _ (File 3) = showString "FileD"
showsPrec _ (File 4) = showString "FileE"
showsPrec _ (File 5) = showString "FileF"
showsPrec _ (File 6) = showString "FileG"
showsPrec _ (File 7) = showString "FileH"
showsPrec d (File n) = showParen (d > 10) $
showString "File " . showsPrec 11 n
instance Enum File where
toEnum n | n >= 0 && n <= 7 = File n
| otherwise = error $ "File out-of-bound " ++ show n
fromEnum (File n) = n
instance Bounded File where
minBound = FileA
maxBound = FileH
pattern FileA, FileB, FileC, FileD, FileE, FileF, FileG, FileH :: File
pattern FileA = File 0
pattern FileB = File 1
pattern FileC = File 2
pattern FileD = File 3
pattern FileE = File 4
pattern FileF = File 5
pattern FileG = File 6
pattern FileH = File 7
{-# COMPLETE FileA, FileB, FileC, FileD, FileE, FileF, FileG, FileH :: File #-}
-------------------------------------------------------------------------------
-- Square
-------------------------------------------------------------------------------
newtype Sq = Sq Int
deriving (Eq, Ord)
instance Ix Sq where
range (Sq i, Sq j) = [Sq k | k <- [i..j]]
index (Sq i, Sq j) (Sq k) = index (i, j) k
inRange (Sq i, Sq j) (Sq k) = inRange (i, j) k
rangeSize (Sq i, Sq j) = j - i
unSq :: Sq -> Int
unSq = coerce
-- TODO: this check is expensive, maybe only worth in "debug" builds.
mkSq :: HasCallStack => Int -> Sq
mkSq n
| n >= 0 && n <= 63 = Sq n
| otherwise = error $ "mkSq " ++ show n
instance Show Sq where
showsPrec d (Sq i)
| i >= 0 && i <= 63 = showString [f', r']
| otherwise = showParen (d > 10) $
showString "Sq " . showsPrec 11 i
where
(r, f) = i `divMod` 8
r' = chr (r + ord '1')
f' = chr (f + ord 'A')
instance Enum Sq where
toEnum n | n >= 0 && n <= 63 = Sq n
| otherwise = error $ "Sq out-of-bound " ++ show n
fromEnum (Sq n) = n
instance Bounded Sq where
minBound = A1
maxBound = H8
pattern A1, A2, A3, A4, A5, A6, A7, A8 :: Sq
pattern B1, B2, B3, B4, B5, B6, B7, B8 :: Sq
pattern C1, C2, C3, C4, C5, C6, C7, C8 :: Sq
pattern D1, D2, D3, D4, D5, D6, D7, D8 :: Sq
pattern E1, E2, E3, E4, E5, E6, E7, E8 :: Sq
pattern F1, F2, F3, F4, F5, F6, F7, F8 :: Sq
pattern G1, G2, G3, G4, G5, G6, G7, G8 :: Sq
pattern H1, H2, H3, H4, H5, H6, H7, H8 :: Sq
pattern A1 = Sq 0
pattern B1 = Sq 1
pattern C1 = Sq 2
pattern D1 = Sq 3
pattern E1 = Sq 4
pattern F1 = Sq 5
pattern G1 = Sq 6
pattern H1 = Sq 7
pattern A2 = Sq 8
pattern B2 = Sq 9
pattern C2 = Sq 10
pattern D2 = Sq 11
pattern E2 = Sq 12
pattern F2 = Sq 13
pattern G2 = Sq 14
pattern H2 = Sq 15
pattern A3 = Sq 16
pattern B3 = Sq 17
pattern C3 = Sq 18
pattern D3 = Sq 19
pattern E3 = Sq 20
pattern F3 = Sq 21
pattern G3 = Sq 22
pattern H3 = Sq 23
pattern A4 = Sq 24
pattern B4 = Sq 25
pattern C4 = Sq 26
pattern D4 = Sq 27
pattern E4 = Sq 28
pattern F4 = Sq 29
pattern G4 = Sq 30
pattern H4 = Sq 31
pattern A5 = Sq 32
pattern B5 = Sq 33
pattern C5 = Sq 34
pattern D5 = Sq 35
pattern E5 = Sq 36
pattern F5 = Sq 37
pattern G5 = Sq 38
pattern H5 = Sq 39
pattern A6 = Sq 40
pattern B6 = Sq 41
pattern C6 = Sq 42
pattern D6 = Sq 43
pattern E6 = Sq 44
pattern F6 = Sq 45
pattern G6 = Sq 46
pattern H6 = Sq 47
pattern A7 = Sq 48
pattern B7 = Sq 49
pattern C7 = Sq 50
pattern D7 = Sq 51
pattern E7 = Sq 52
pattern F7 = Sq 53
pattern G7 = Sq 54
pattern H7 = Sq 55
pattern A8 = Sq 56
pattern B8 = Sq 57
pattern C8 = Sq 58
pattern D8 = Sq 59
pattern E8 = Sq 60
pattern F8 = Sq 61
pattern G8 = Sq 62
pattern H8 = Sq 63
-------------------------------------------------------------------------------
-- IsSquare
-------------------------------------------------------------------------------
class IsSquare sq where
toSq :: sq -> Sq
fromSq :: Sq -> sq
file :: sq -> File
file = snd . fromSq . toSq
rank :: sq -> Rank
rank = fst . fromSq . toSq
instance IsSquare Sq where
toSq = id
fromSq = id
instance (rank ~ Rank, file ~ File) => IsSquare (rank, file) where
toSq (Rank r, File f) = mkSq (r*8 + f)
fromSq (Sq i) = case i `divMod` 8 of
(r, f) -> (Rank r, File f)
rank = fst
file = snd
toIndex :: forall sq. IsSquare sq => sq -> Int
toIndex = coerce (toSq :: sq -> Sq)
toRF :: forall sq. IsSquare sq => sq -> (Int, Int)
toRF (fromSq . toSq -> (Rank r, File f)) = (r, f)
isDark :: IsSquare sq => sq -> Bool
isDark sq = (0xaa55aa55aa55aa55 :: Word64) `testBit` toIndex sq
isLight :: IsSquare sq => sq -> Bool
isLight = not . isDark
toCoord :: (IsSquare sq, IsString s) => sq -> s
toCoord (toRF -> (r,f)) = fromString [chr (f + ord 'a'), chr (r + ord '1')]
and adjusting everything else to use this.
IMHO it made library API nicer.