module Rubik.Cube.Facelet.Internal where import Rubik.Misc import Control.Monad import Data.Char ( intToDigit ) import Data.List import qualified Data.Vector.Unboxed as U -- | There are @54 == 6 * 9@ facelets. numFacelets :: Int numFacelets = 6 * 9 -- | Cube as a permutation of facelets (replaced-by). -- -- Every facelet is represented as an 'Int' in @[0 .. 54]@. newtype Facelets = Facelets { -- | The underlying 'Vector' of 'Int'. fromFacelets :: Vector Int } deriving (Eq, Show) instance Monoid Facelets where mempty = Facelets $ idVector numFacelets mappend (Facelets b) (Facelets c) = Facelets $ composeVector b c instance Group Facelets where inverse (Facelets a) = Facelets $ inverseVector a -- | See 'fromFacelets'' fromFacelets' :: Facelets -> [Int] fromFacelets' = U.toList . fromFacelets -- | See 'facelets'. facelets' :: [Int] -> Maybe Facelets facelets' = facelets . U.fromList -- | This constructor checks that the input is a permutation of '[0 .. 53]'. facelets :: Vector Int -> Maybe Facelets facelets v = do guard $ U.length v == numFacelets && isPermutationVector v return (Facelets v) -- | Constructor with no safety checks unsafeFacelets = Facelets unsafeFacelets' = Facelets . U.fromList -- | The standard cube colors are the values between @0@ and @5@. type Color = Int -- | Cube as a list of facelet colors. newtype ColorFacelets = ColorFacelets { -- | The underlying 'Vector' of 'Color'. fromColorFacelets :: Vector Color } deriving (Eq, Show) -- | See 'fromColorFacelets'. fromColorFacelets' :: ColorFacelets -> [Color] fromColorFacelets' = U.toList . fromColorFacelets -- | See 'colorFacelets'. colorFacelets' :: [Color] -> Maybe ColorFacelets colorFacelets' = colorFacelets . U.fromList -- | This constructor checks that only standard colors (in @[0 .. 5]@) -- are used, that the argument has length @54@ and that the centers -- are colored in order. -- -- Note that there may still be more or less than 9 colors of a kind, -- although that cannot be the case in an actual cube. colorFacelets :: Vector Color -> Maybe ColorFacelets colorFacelets v = do guard $ U.length v == numFacelets && U.all (\c -> 0 <= c && c < 6) v && map (v U.!) centerFacelets == [0 .. 5] return (ColorFacelets v) -- | The color of a facelet given its identifier. colorOf :: Int -> Color colorOf = (`div` 9) -- | Remove permutation information. -- -- If the argument cube can be obtained from the solved cube with the usual moves, -- then the original permutation can be recovered with 'Cubie.colorFaceletsToCube'. colorFaceletsOf :: Facelets -> ColorFacelets colorFaceletsOf = ColorFacelets . U.map colorOf . fromFacelets -- | A color is mapped to a face, indicated by a @Char@: -- -- > map colorChar [0..5] == "ULFRBD" colorChar :: Color -> Char colorChar = ("ULFRBD" !!) -- | String listing the permutation of facelets numbered in base 9. -- -- Base 9 is convenient here because the first digit directly corresponds to a face -- and the second to the facelet position in that face. stringOfFacelets :: Facelets -> String stringOfFacelets = intercalate " " . map base9 . U.toList . fromFacelets where base9 n = map intToDigit [n `div` 9, n `mod` 9] -- | String listing the facelet colors. stringOfColorFacelets :: ColorFacelets -> String stringOfColorFacelets = intercalate " " . chunk 9 . map colorChar . U.toList . fromColorFacelets -- | Only show the colors of the facelets. stringOfColorFacelets' :: Facelets -> String stringOfColorFacelets' = stringOfColorFacelets . colorFaceletsOf -- -- | Convert a 6-color list of length 54 in any representation which implements 'Eq' -- to 'ColorFacelets'. colorFacelets'' :: Eq a => [a] -> Maybe ColorFacelets colorFacelets'' colors = do guard (length colors == numFacelets) guard (length (nub centers) == 6) colorFacelets' =<< sequence ((`lookup` zip centers [0 .. 5]) <$> colors) where centers = (colors !!) <$> centerFacelets -- -- Facelets corresponding to each cubie -- | -- @ -- centerFacelets -- = [ 4, -- U -- 13, -- L -- 22, -- F -- 31, -- R -- 40, -- B -- 49] -- D -- @ centerFacelets :: [Int] centerFacelets = [4, 13 .. 49] -- | Corner facelets ulb, ufl, urf, ubr, dlf, dfr, drb, dbl :: [Int] ulb = [ 0, 9, 38] ufl = [ 6, 18, 11] urf = [ 8, 27, 20] ubr = [ 2, 36, 29] dlf = [45, 17, 24] dfr = [47, 26, 33] drb = [53, 35, 42] dbl = [51, 44, 15] -- | > cornerFacelets = [ulb, ufl, urf, ubr, dlf, dfr, drb, dbl] cornerFacelets :: [[Int]] cornerFacelets = [ulb, ufl, urf, ubr, dlf, dfr, drb, dbl] -- | Edge facelets ul, uf, ur, ub, dl, df, dr, db, fl, fr, bl, br :: [Int] ul = [ 3, 10] uf = [ 7, 19] ur = [ 5, 28] ub = [ 1, 37] dl = [48, 16] df = [46, 25] dr = [50, 34] db = [52, 43] fl = [21, 14] fr = [23, 30] bl = [41, 12] br = [39, 32] -- | > edgeFacelets = [ul, uf, ur, ub, dl, df, dr, db, fl, fr, bl, br] edgeFacelets :: [[Int]] edgeFacelets = [ul, uf, ur, ub, dl, df, dr, db, fl, fr, bl, br]