packages feed

cube-hs-0.4.0.0: src/Data/Cube/Internal.hs

{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE GADTs #-}

module Data.Cube.Internal 
(
  Color(..)
, Face(..)
, Turn(..)
, Move(..)
, Cube
, ActsOn(..)
, Actionable(..)
, moveFaces
, moveFromRaw
, unsafeMoveFromRaw
, showAction
, cubeId
, isLegalCubeString
, showCube
, parseCube
, applyTurns
, showTurns
, parseTurns
, solve
, unsafeSolve
, solveFrom
, unsafeSolveFrom
)
where 

import Data.Cube.Def
import Data.Cube.FFI

import qualified Data.Vector.Sized as V
import Data.Maybe (fromMaybe)
import System.IO.Unsafe (unsafePerformIO)
import Foreign (allocaBytes)
import Foreign.C (peekCString)
import Foreign.C.String (withCString)
import Foreign.C.Types (CBool(..))


cubeId :: Cube
cubeId = Cube (fromMaybe (error "cId") (V.fromList (concatMap (replicate 9) [U .. B])))

showCube :: Cube -> String
showCube (Cube v) = concatMap show (V.toList v)

parseCube :: String -> Maybe Cube
parseCube cubeStr
    | isLegalCubeString cubeStr = do
        colors <- mapM charToColor cubeStr
        Cube <$> V.fromList colors
    | otherwise = Nothing
    where
        charToColor :: Char -> Maybe Color
        charToColor c = case c of
            'U' -> Just U
            'R' -> Just R
            'F' -> Just F
            'D' -> Just D
            'L' -> Just L
            'B' -> Just B
            _ -> Nothing

isLegalCubeString :: String -> Bool
isLegalCubeString str = 
    length str == 54 &&
    unsafePerformIO (withCString str $ \c_str -> do
        CBool res <- c_solvable c_str
        return (res /= 0))

applyTurns :: Cube -> [Turn] -> Cube
applyTurns = foldl (&>)

parseTurns :: String -> Maybe [Turn]
parseTurns str = mapM stringToTurn (words str) where 
    stringToTurn :: String -> Maybe Turn 
    stringToTurn s = case s of 
        "U" -> Just Ux1; "U2" -> Just Ux2; "U'" -> Just Ux3
        "R" -> Just Rx1; "R2" -> Just Rx2; "R'" -> Just Rx3
        "F" -> Just Fx1; "F2" -> Just Fx2; "F'" -> Just Fx3
        "D" -> Just Dx1; "D2" -> Just Dx2; "D'" -> Just Dx3
        "L" -> Just Lx1; "L2" -> Just Lx2; "L'" -> Just Lx3
        "B" -> Just Bx1; "B2" -> Just Bx2; "B'" -> Just Bx3
        _ -> Nothing

showTurns :: [Turn] -> String 
showTurns ts = unwords (map turnToString ts) where 
    turnToString :: Turn -> String 
    turnToString t = case t of 
        Ux1 -> "U"; Ux2 -> "U2"; Ux3 -> "U'"
        Rx1 -> "R"; Rx2 -> "R2"; Rx3 -> "R'"
        Fx1 -> "F"; Fx2 -> "F2"; Fx3 -> "F'"
        Dx1 -> "D"; Dx2 -> "D2"; Dx3 -> "D'"
        Lx1 -> "L"; Lx2 -> "L2"; Lx3 -> "L'"
        Bx1 -> "B"; Bx2 -> "B2"; Bx3 -> "B'"

solve :: Cube -> Cube -> Either String [Turn]
solve src tgt = unsafePerformIO $
    withCString (showCube src) $ \c_src ->
    withCString (showCube tgt) $ \c_tgt ->
    allocaBytes 128 $ \c_buf -> do
        resCode <- c_solve c_buf c_src c_tgt 30 (CBool 1)
        if resCode == 0
            then parseCSolution c_buf
            else handleErr resCode  
    where
        parseCSolution buf = do 
            str <- peekCString buf
            return $ case parseTurns str of
                Just turns  -> Right turns
                Nothing     -> Left $ "failed to parse solver string: " ++ show str
    
        handleErr code = do 
            errPtr <- c_solve_result_to_string code
            errStr <- peekCString errPtr
            return $ Left $ "Solver error (" ++ show code ++ "): "++ errStr


unsafeSolve :: Cube -> Cube -> [Turn]
unsafeSolve src tgt = case solve src tgt of 
    Left err -> error err
    Right ts -> ts

solveFrom :: Cube -> Either String [Turn]
solveFrom c = solve c cubeId 

unsafeSolveFrom :: Cube -> [Turn]
unsafeSolveFrom c = unsafeSolve c cubeId