Intro
Recently, we were taking a break with a colleague by trying to compute the number of states of Rubik’s cube. After a while, we decided to consider a simpler problem, so Rubik’s triangle was born.
It’s initial state:
B↑
1/ \3
A↑ − C↑
2
You can swap corners along edges 1,2,3. Swapping along 1 leads to
A↓
/ \
B↓ − C↑
If you then swap along 2:
A↓
/ \
C↑ − B↓
Note that each time you swap, not only letters (A,B,C) are swapped between corners, but so does the orientation of corners (↑ or ↓). This adds a constraint to imitate constraints that you have in Rubik’s cube.
The goal of the game is to go back to the initial state after many random swaps. Here is your goal:
B↑
/ \
A↑ − C↑
Play it with Haskell
For your pleasure, I’ve coded this game in Haskell. Run it with runhaskell rubik_triangle.hs
or compile it ghc rubik_triangle.hs
.
```haskell
import System.IO (BufferMode(NoBuffering), stdin, hSetBuffering)
import System.Random (newStdGen, randomRs)
import Data.Char (digitToInt)
import Control.Monad (when)
data Orientation = Up | Down deriving (Eq)
instance Show Orientation where
show Up = "↑"
show Down = "↓"
data Letter = A | B | C deriving (Show, Eq)
data Angle = Angle Orientation Letter deriving (Eq)
instance Show Angle where
show (Angle o l) = show l ++ show o
data Triangle = Triangle Angle Angle Angle deriving (Eq)
instance Show Triangle where
show (Triangle a b c) =
" "++show b++" \n" ++
" /"++" "++"\\ \n" ++
" "++show a++" −"++" "++show c
flipO :: Orientation -> Orientation
flipO Up = Down
flipO Down = Up
trans :: (Angle, Angle) -> (Angle, Angle)
trans (Angle o1 l1, Angle o2 l2) = (Angle (flipO o1) l2, Angle (flipO o2) l1)
transition :: Int -> Triangle -> Triangle
transition 1 (Triangle a b c) = Triangle a' b' c
where (a', b') = trans (a, b)
transition 2 (Triangle a c b) = Triangle a' c b'
where (a', b') = trans (a, b)
transition 3 (Triangle c a b) = Triangle c a' b'
where (a', b') = trans (a, b)
seqToTransition :: [Int] -> (Triangle -> Triangle)
seqToTransition [] = id
seqToTransition (n:ns)
| n `elem` [1, 2, 3] = transition n . seqToTransition ns
| otherwise = error "Bad n"
tr0 = Triangle (Angle Up A) (Angle Up B) (Angle Up C)
loop :: Triangle -> IO ()
loop tr = do
print tr
putStrLn "\n"
when (tr == tr0) $ putStrLn "It is solved!"
x <- getChar
putStrLn ("You pressed: " ++ [x])
if x `elem` ['1', '2', '3']
then loop $ transition (digitToInt x) tr
else case x of
'q' -> return ()
'h' -> do
putStrLn helpMsg
loop tr
otherwise -> loop tr
helpMsg = "\n\nPress 1,2,3 to play, q to quit. h to print this message.\n\n"
main :: IO ()
main = do
hSetBuffering stdin NoBuffering
putStrLn helpMsg
putStrLn "Initial solved state state:\n"
print tr0
putStrLn "\nMixing it:\n"
g <- newStdGen
let moves = randomRs (1, 3 :: Int) g
trMixed = seqToTransition (take 40 moves) $ tr0
loop trMixed
```