packages feed

twisty-0.1.0: Twisty/Cube4.hs

{-
Copyright 2011 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-}

{-# LANGUAGE TypeFamilies #-}

-- | Defines the 4x4 cube puzzle.
module Twisty.Cube4 where

import Twisty.Cycles
import Twisty.Cube
import Twisty.FaceTwist
import Twisty.Group
import qualified Twisty.Memo as Memo
import Twisty.Polyhedron
import Twisty.Puzzle
import Twisty.Wreath
import Twisty.Twists
import Twisty.Zn

import Control.Monad (mapM)
import Data.Array.IArray ((!), Array, listArray)
import Data.Char (isUpper, toLower, toUpper)
import Data.Ix (Ix)
import Data.List (elemIndex, transpose)
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Maybe (fromJust, listToMaybe, maybeToList)
import Data.Monoid (Monoid, mappend, mempty)
import GHC.Enum (boundedEnumFrom, boundedEnumFromThen)

newtype Cube4 = Cube4 (Wreath Vertex, Wreath EdgePiece, Wreath FacePiece) deriving (Eq, Ord)

instance Monoid Cube4 where
  mempty = Cube4 one
  mappend (Cube4 s1) (Cube4 s2) = Cube4 (s1 $* s2)

instance Group Cube4 where
  ginvert (Cube4 s) = Cube4 $ ginvert s

-- | The face pieces, four for each of the cube's faces.
newtype FacePiece = FacePiece Int deriving (Eq, Ord, Ix)

-- | The edge pieces, two for each of the cube's edges.
newtype EdgePiece = EdgePiece Int deriving (Eq, Ord, Ix)

instance Enum FacePiece where
  toEnum = toBoundedEnum FacePiece
  fromEnum (FacePiece i) = i
  enumFrom = boundedEnumFrom
  enumFromThen = boundedEnumFromThen

instance Bounded FacePiece where
  minBound = FacePiece 0
  maxBound = FacePiece $ length allFacePiecesAsFaces - 1

instance WreathPermutable FacePiece where
  type WreathTwist FacePiece = Twistless

allFacePiecesAsFaces = concat [faceVerticesAsFaces f | f <- [minBound..]]

facePieceFaces :: FacePiece -> [Face]
facePieceFaces = (facesArray !) . fromEnum
  where facesArray = makeFacesArray allFacePiecesAsFaces

facesToMaybeFacePiece :: [Face] -> Maybe FacePiece
facesToMaybeFacePiece (f:fs) = (f, facesIndex fs) `Map.lookup` fpMap
  where fpMap :: Map (Face, Int) FacePiece
        fpMap = Map.fromList [((f, i), fp) | fp <- [minBound..],
                              let (f:fs) = facePieceFaces fp,
                              let i = facesIndex fs]

facesToFacePiece :: [Face] -> FacePiece
facesToFacePiece = fromJust . facesToMaybeFacePiece

facePieceFace :: FacePiece -> Face
facePieceFace = toEnum . (`div` 4) . fromEnum

faceFacePieces :: Face -> [FacePiece]
faceFacePieces = (piecesArray !)
  where piecesArray :: Array Face [FacePiece]
        piecesArray = listArray (minBound, maxBound)
                      [map facesToFacePiece $ faceVerticesAsFaces f | f <- [minBound..]]

instance Show FacePiece where
  showsPrec _ = showString . toString
    where toString fp = let (fn:fns) = map faceToName $ facePieceFaces fp
                        in fn:'.':fns

instance Read FacePiece where
  readsPrec _ (c1:dot:c2:c3:cs)
   | dot == '.' = maybeToList $ do
       fs <- mapM (nameToMaybeFace . toLower) [c1, c2, c3]
       fp <- facesToMaybeFacePiece fs
       return (fp, cs)
  readsPrec _ _ = []

instance Enum EdgePiece where
  toEnum = toBoundedEnum EdgePiece
  fromEnum (EdgePiece i) = i
  enumFrom = boundedEnumFrom
  enumFromThen = boundedEnumFromThen

instance Bounded EdgePiece where
  minBound = EdgePiece 0
  maxBound = EdgePiece $ length allEdgePiecesAsFaces - 1

instance WreathPermutable EdgePiece where
  type WreathTwist EdgePiece = Twistless

allEdgePiecesAsFaces = concat [pieces e | e <- [minBound..]]
  where pieces :: Edge -> [[Face]]
        pieces e = let [f, n] = edgeFaces e
                   in [[f, n, previousNeighbor f n], [f, n, nextNeighbor f n]]

edgePieceFaces :: EdgePiece -> [Face]
edgePieceFaces = (facesArray !) . fromEnum
  where facesArray = makeFacesArray allEdgePiecesAsFaces

facesToMaybeEdgePiece :: [Face] -> Maybe EdgePiece
facesToMaybeEdgePiece [f1, f2, f] = (facesIndex [f1, f2], f) `Map.lookup` epMap
  where epMap :: Map (Int, Face) EdgePiece
        epMap = Map.fromList [((i, f), ep) | ep <- [minBound..],
                              let [f1,f2,f] = edgePieceFaces ep,
                              let i = facesIndex [f1,f2]]

facesToEdgePiece :: [Face] -> EdgePiece
facesToEdgePiece = fromJust . facesToMaybeEdgePiece

edgePieceEdge :: EdgePiece -> Edge
edgePieceEdge = toEnum . (`div` 2) . fromEnum

instance Show EdgePiece where
  showsPrec _ = showString . toString
    where toString ep = let (c1:c2:cs) = map faceToName (edgePieceFaces ep)
                        in c1:c2:'.':cs

instance Read EdgePiece where
  readsPrec _ (c1:c2:dot:c3:cs)
   | dot == '.' = maybeToList $ do
       fs <- mapM (nameToMaybeFace . toLower) [c1, c2, c3]
       ep <- facesToMaybeEdgePiece fs
       return (ep, cs)
  readsPrec _ _ = []


instance Puzzle Cube4 where
  type Move Cube4 = CubeMove2
  fromMove = Memo.array fromMove2
    where fromMove2 :: CubeMove2 -> Cube4
          fromMove2 (FaceTwist f 1 0) = Cube4 (vw, ew, fw)
            where vw = fromCycles [asCycle' f faceVertices vertexFaces]
                  ew = fromCycles $ map edgeCycle edgePieces
                  fw = fromCycles [asSimpleCycle $ faceFacePieces f]
                  edgeCycle eps = asCycle f eps edgePieceFaces
                  edgePieces = transpose $ map (map facesToEdgePiece)
                               [[[f, f2, f3], [f, f3, f2]] | [_, f2, f3] <- faceVerticesAsFaces f]

          fromMove2 (FaceTwist f 1 1) = Cube4 (one, ew, fw) $* fromMove (FaceTwist f 1 0)
            where ew = fromCycles [asCycle f edgePieces edgePieceFaces]
                  fw = fromCycles $ map asSimpleCycle facePieces
                  edgePieces = map facesToEdgePiece
                               [[f2, f3, f] | [_, f2, f3] <- faceVerticesAsFaces f]
                  facePieces = transpose $ map (map facesToFacePiece)
                               [[[f2, f3, f], [f3, f2, f]] | [_, f2, f3] <- faceVerticesAsFaces f]

          fromMove2 (FaceTwist f n d) = fromMove (FaceTwist f 1 d) $^ n


instance Show Cube4 where
  showsPrec _ (Cube4 (v, e, f)) = fromOptCycles $ optShowCycles v $* optShowCycles e $* optShowCycles f

c4 :: String -> Algorithm Cube4
c4 = read