1 -- This file is part of Intricacy
2 -- Copyright (C) 2013 Martin Bays <mbays@sdf.org>
4 -- This program is free software: you can redistribute it and/or modify
5 -- it under the terms of version 3 of the GNU General Public License as
6 -- published by the Free Software Foundation.
8 -- You should have received a copy of the GNU General Public License
9 -- along with this program. If not, see http://www.gnu.org/licenses/.
13 An abstract hex board type.
15 We coordinatize by the integral points of the hyperplane x+y+z=0:
17 Some hopefully elucidatory diagrams:
28 | 1 -. . * * , * : "principal hextant"
29 . . . Y 0 -. . 0 . . x>=0&&y>0
38 {-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FlexibleContexts #-}
42 import Data.Semigroup as Sem
45 import Data.List (minimumBy)
46 import Data.Function (on)
48 data HexVec = HexVec {hx,hy,hz :: Int} deriving (Eq, Ord, Show, Read)
55 hv2tup :: HexVec -> (Int,Int,Int)
56 hv2tup (HexVec x y z) = (x,y,z)
58 tup2hv :: (Int,Int,Int) -> HexVec
60 | x+y+z == 0 = HexVec x y z
61 | otherwise = error "bad hex"
63 hv2tupxy :: HexVec -> (Int,Int)
64 hv2tupxy (HexVec x y _) = (x,y)
66 tupxy2hv :: (Int,Int) -> HexVec
67 tupxy2hv (x,y) = HexVec x y (-(x+y))
69 hexLen :: HexVec -> Int
70 hexLen (HexVec x y z) = maximum $ abs <$> [x,y,z]
72 hexDot :: HexVec -> HexVec -> Int
73 hexDot (HexVec x y z) (HexVec x' y' z') = x*x'+y*y'+z*z'
75 hexDisc :: Int -> [HexVec]
76 hexDisc r = [ HexVec x y z | x <- [-r..r], y <- [-r..r],
77 let z = -x-y, abs z <= r ]
79 hextant :: HexVec -> Int
86 hextant (HexVec x y z)
88 | -z > 0 && -x >= 0 = 1
90 | -x > 0 && -y >= 0 = 3
92 | -y > 0 && -z >= 0 = 5
93 | otherwise = error "Tried to take hextant of zero"
95 -- hextant (rotate n hu) == n
96 rotate :: Int -> HexVec -> HexVec
98 rotate 2 (HexVec x y z) = HexVec z x y
99 rotate (-2) (HexVec x y z) = HexVec y z x
100 rotate 1 v = neg $ rotate (-2) v
101 rotate (-1) v = neg $ rotate 2 v
102 rotate n v | n < 0 = rotate (n+6) v
103 | n > 6 = rotate (n-6) v
104 | otherwise = rotate (n-2) (rotate 2 v)
106 cmpAngles :: HexVec -> HexVec -> Ordering
107 -- ^ordered by angle, taking cut along u
108 cmpAngles v@(HexVec x y _) v'@(HexVec x' y' _)
109 | v == zero && v' == zero = EQ
111 | hextant v /= hextant v' =
112 compare (hextant v) (hextant v')
114 cmpAngles (rotate (-(hextant v)) v) (rotate (-(hextant v)) v')
115 | otherwise = compare (y%x) (y'%x')
117 instance Ix HexVec where
119 [ tupxy2hv (x,y) | (x,y) <- range (hv2tupxy h, hv2tupxy h') ]
121 inRange (hv2tupxy h, hv2tupxy h') (hv2tupxy h'')
123 index (hv2tupxy h , hv2tupxy h') (hv2tupxy h'')
125 -- HexDirs are intended to be HexVecs of length <= 1
127 isHexDir :: HexVec -> Bool
128 isHexDir v = hexLen v == 1
130 type HexDirOrZero = HexVec
131 isHexDirOrZero :: HexVec -> Bool
132 isHexDirOrZero v = hexLen v <= 1
135 hexDirs = (`rotate` hu) <$> [0..5]
137 hexVec2HexDirOrZero :: HexVec -> HexDirOrZero
138 hexVec2HexDirOrZero v
140 | otherwise = rotate (hextant v) hu
142 --minusHu = HexVec (-1) 1 0
143 --minusHv = HexVec 0 (-1) 1
144 --minusHw = HexVec 1 0 (-1)
146 canonDir :: HexDir -> HexDir
147 canonDir dir | dir `elem` [ hu, hv, hw ] = dir
148 | isHexDir dir = canonDir $ neg dir
150 | otherwise = undefined
152 scaleToLength :: Int -> HexVec -> HexVec
153 scaleToLength n v@(HexVec x y z) =
156 lv' = (`div`l) . (n*) <$> [x,y,z]
157 minI = fst $ minimumBy (compare `on` snd) $
158 zip [0..] $ abs <$> lv'
159 [x'',y'',z''] = zipWith (-) lv' [ d
161 , let d = if i == minI then sum lv' else 0 ]
162 in HexVec x'' y'' z''
163 truncateToLength :: Int -> HexVec -> HexVec
164 truncateToLength n v = if hexLen v <= n then v else scaleToLength n v
167 Some general stuff on groups and actions and principal homogeneous spaces. We
168 use additive notation, even though there's no assumption of commutativity.
171 class Monoid g => Grp g where
176 instance (Grp g1, Grp g2) => Grp (g1,g2) where
177 neg (a,b) = (neg a, neg b)
181 class Action a b where
183 instance Monoid m => Action m m where
186 class Differable a b c where
188 instance Grp g => Differable g g g where
191 newtype PHS g = PHS { getPHS :: g }
192 deriving (Eq, Ord, Show, Read)
194 instance Grp g => Action g (PHS g) where
195 x +^ (PHS y) = PHS (x +^ y)
196 instance Grp g => Differable (PHS g) (PHS g) g where
197 (PHS x) -^ (PHS y) = x -^ y
200 class MultAction a b where
203 instance (Grp a, Integral n) => MultAction n a where
207 | n < 0 = (-n) *^ neg x
208 | even n = (n `div` 2) *^ (x +^ x)
209 | otherwise = x +^ ((n `div` 2) *^ (x +^ x))
213 Now we define HexSpaces as spaces acted on by HexVec, and with a canonical
214 HexVec difference between two points (e.g. PHS HexVec).
218 instance Sem.Semigroup HexVec where
219 (HexVec x y z) <> (HexVec x' y' z') = HexVec (x+x') (y+y') (z+z')
220 instance Monoid HexVec where
221 mempty = HexVec 0 0 0
223 instance Grp HexVec where
224 neg (HexVec x y z) = HexVec (-x) (-y) (-z)
226 class (Action HexVec b, Differable b b HexVec) => HexSpace b
227 instance HexSpace (PHS HexVec)
229 type HexPos = PHS HexVec
239 r = range (tup2hv (-3,-3,6), tup2hv (3,3,-6))
240 test1 = index (tup2hv (-3,-3,6), tup2hv (3,3,-6)) (r!!5) == 5