packages feed

hstzaar-0.9: src/Serialize.hs

-- Serialization of Tzaar games to/from XML 
-- Pedro Vasconcelos, 2012
module Serialize where
import Data.Version
import Paths_hstzaar(version)
import Data.Maybe
import Text.XML.Light
import qualified Data.Map as Map
import Board

-- | convertion to/from XML elements
class ToXML a where
  toXML :: a -> Element
  
class FromXML a where  
  fromXML :: Element -> Maybe a


-- | wrapper for a piece together with a board position   
newtype PosPiece = PosPiece (Position, Piece) 
                 deriving (Eq,Show)

-- | wrapper for a list of moves from game start
newtype MoveList = MoveList [Move] deriving (Eq,Show)

-- | wrapper for numbered & labelled moves
newtype NumMove = NumMove (Int,Color,Move) deriving (Eq,Show)

instance Node PosPiece where
  node qn (PosPiece (pos,(c,k,h))) = add_attrs alist $ node qn ()
    where alist = [attr "color" (show c), 
                   attr "kind" (show k),
                   attr "height" (show h),
                   attr "position" (show pos)]

instance Node Move where
  node qn m = node qn $ Text (cdata $ showMove m)

instance Node NumMove where
  node qn (NumMove (n,c,m)) = add_attrs alist $ node qn $ Text (cdata $ showMove m)
    where alist = [attr "num" (show n), attr "color" (show c)]

instance Node MoveList where
  node qn (MoveList ms) = node qn $ map (Elem . node (unqual "move")) nms
    where nms = map NumMove (zip3 [1..] cs ms)
          cs = White : cycle [Black,Black,White,White]


instance Node Board where
  node qn b = node qn $ 
              map (Elem . node (unqual "piece") . PosPiece) $ 
              Map.assocs (pieces b)

instance Node Game where
  node qn g = add_attrs alist  $ 
              node qn [Elem (node (unqual "board") (initial g)),
                       Elem (node (unqual "moves") (MoveList ms))]
    where ms = reverse (trail g) ++ remain g -- all moves
          alist =[attr "version" (showVersion version),
                  attr "human" (show (human g))]


instance ToXML Game where
  toXML = node (unqual "hstzaar") 

instance FromXML Board where  
  fromXML el = return (initBoard assocs)
    where assocs = catMaybes
            [do { c<-readAttr (unqual "color") el' 
                ; k<-readAttr (unqual "kind") el'
                ; h<-readAttr (unqual "height") el'
                ; pos<-readAttr (unqual "position") el'
                ; return (pos,(c,k,h))
                } | el'<-findChildren (unqual "piece") el]
          

instance FromXML MoveList where
  fromXML el = Just (MoveList moves)
    where moves = catMaybes 
                  [ readMove (strContent el') 
                  | el'<-findChildren (unqual "move") el]


instance FromXML Game where
  fromXML el = do el1 <- findChild (unqual "board") el
                  el2 <- findChild (unqual "moves") el
                  c <- readAttr (unqual "human") el  -- human player color
                  b <- fromXML el1
                  MoveList ms <- fromXML el2
                  let g = initGame b c
                  let b' = foldr applyMoveSkip b (reverse ms)
                  return g { board=b', trail=reverse ms }


showXML :: ToXML a => a -> String
showXML a = ppTopElement (toXML a)

readXML :: FromXML a => String -> Maybe a
readXML txt = parseXMLDoc txt >>= fromXML 



-- | parse an attribute from an element
readAttr :: Read a => QName -> Element -> Maybe a          
readAttr name el = do txt<-findAttr name el 
                      case reads txt of
                        [] -> Nothing
                        (a,_):_ -> Just a


readMove :: String -> Maybe Move
readMove [a,b,'x',c,d] = do (from,_) <- listToMaybe (reads [a,b])
                            (to,_) <- listToMaybe (reads [c,d])
                            return (Capture from to)
  
readMove [a,b,'-',c,d] = do (from,_) <- listToMaybe (reads [a,b])
                            (to,_) <- listToMaybe (reads [c,d])
                            return (Stack from to)
readMove "pass"          = Just Pass  
readMove "skip"          = Just Skip
readMove _               = Nothing



-- | make an attribute with an unqualified name
attr :: String -> String -> Attr
attr n = Attr (unqual n) 

-- | make a cdata from a string
cdata :: String -> CData
cdata txt = CData CDataText txt Nothing