{- ルービックキューブ : rubic.hs -}

import List

-- 2面体と3面体の型
data Obj = TE | TS | TW | TN | ET | ES | EB | EN | ST | SE | SB | SW
         | BE | BS | BW | BN | WT | WS | WB | WN | NT | NE | NB | NW
         | TES | EST | STE | TSW | SWT | WTS | TWN | WNT | NTW | TNE
         | NET | ETN | BSE | SEB | EBS | BEN | ENB | NBE | BNW | NWB
         | WBN | BWS | WSB | SBW deriving (Eq, Show)

allObj :: [Obj]         -- すべての2面体と3面体のリスト
allObj = [TE, TS, TW, TN, ET, ES, EB, EN, ST, SE, SB, SW, 
          BE, BS, BW, BN, WT, WS, WB, WN, NT, NE, NB, NW,
          TES, EST, STE, TSW, SWT, WTS, TWN, WNT, NTW, TNE,
          NET, ETN, BSE, SEB, EBS, BEN, ENB, NBE, BNW, NWB,
          WBN, BWS, WSB, SBW]

goesTo :: Obj -> [[Obj]] -> Obj
goesTo c [] = c
goesTo c (s:ss) 
  | length x == 0     = goesTo c ss
  | y == length s - 1 = goesTo (s!!0) ss
  | otherwise         = goesTo (s!!(y + 1)) ss
                          where [y] = x
                                x = elemIndices c s

assoc :: Obj -> [(Obj,Obj)] -> (Obj,Obj)
assoc c ((x,y):ss)
  | c == x    = (x,y)
  | otherwise = assoc c ss

-- makeCycle multiplies permutations in cycle form   置換の積を計算

makeCycle0 :: [(Obj,Obj)] -> [[Obj]] -> [[Obj]]
makeCycle0 [] qs = qs
makeCycle0 ((x,y):ss) qs
  | x == y   = makeCycle0 ss qs
  |otherwise = makeCycle1 ss ([x,y]:qs)

makeCycle1 :: [(Obj,Obj)] -> [[Obj]] -> [[Obj]]
makeCycle1 ss (cs:css) 
  | c == head cs  = makeCycle0 ss' (cs:css)
  | otherwise     = makeCycle1 ss' ((cs ++ [c]):css)
        where c = snd d
              d = assoc (last cs) ss
              ss' = delete d ss

prodPerm :: [[[Obj]]] -> [[Obj]]       -- 各種の手順による置換の計算
prodPerm ops = makeCycle0 (zip allObj allGo) []
            where allGo = map (\x -> goesTo x (concat ops)) allObj

e,e',s,s',w,w',n,n',t,t',b,b' :: [[Obj]] -- e(東)などの面を時計回りに90度回転
e = [[SE,TE,NE,BE],[ES,ET,EN,EB],
     [TES,NET,BEN,SEB],[STE,TNE,NBE,BSE],[EST,ETN,ENB,EBS]]
s = [[WS,TS,ES,BS],[SW,ST,SE,SB],
     [TSW,EST,BSE,WSB],[WTS,TES,EBS,BWS],[SWT,STE,SEB,SBW]]
w = [[NW,TW,SW,BW],[WN,WT,WS,WB],
     [TWN,SWT,BWS,NWB],[NTW,TSW,SBW,BNW],[WNT,WTS,WSB,WBN]]
n = [[EN,TN,WN,BN],[NE,NT,NW,NB],
     [TNE,WNT,BNW,ENB],[ETN,TWN,WBN,BEN],[NET,NTW,NWB,NBE]]
t = [[ST,WT,NT,ET],[TS,TW,TN,TE],
     [WTS,NTW,ETN,STE],[SWT,WNT,NET,EST],[TSW,TWN,TNE,TES]]
b = [[SB,EB,NB,WB],[BS,BE,BN,BW],
     [EBS,NBE,WBN,SBW],[SEB,ENB,NWB,WSB],[BSE,BEN,BNW,BWS]]
e' = prodPerm [e,e,e]  -- e'などはeなどの反時計回りの回転
s' = prodPerm [s,s,s]
w' = prodPerm [w,w,w]
n' = prodPerm [n,n,n]
t' = prodPerm [t,t,t]
b' = prodPerm [b,b,b]

