definition module Sokoban
-:: SokobanPuzzle = {
- width :: Int,
- height :: Int,
- data :: [[SokobanTile]]}
+from StdString import class toString
+
+:: SokobanPuzzle = Sokoban [[SokobanTile]]
:: SokobanTile = Wall | Free | Box | Target | Agent
+instance toString SokobanTile
+instance toString SokobanPuzzle
+
parse :: String *World -> (SokobanPuzzle, *World)
--- /dev/null
+implementation module Sokoban
+
+import StdFile
+import StdList
+import StdString
+import StdMisc
+
+Start :: *World -> (String, *World)
+Start w
+# (s, w) = parse "../sokobanzip/screens/screen.1" w
+= ("\n" +++ toString s, w)
+
+instance toString SokobanPuzzle where
+ toString (Sokoban []) = ""
+ toString (Sokoban [[]:rows]) = "\n" +++ toString (Sokoban rows)
+ toString (Sokoban [[x:xs]:rows]) =
+ toString x +++ toString (Sokoban [xs:rows])
+
+instance toString SokobanTile where
+ toString Free = " "
+ toString Wall = "#"
+ toString Box = "$"
+ toString Agent = "@"
+ toString Target = "."
+
+parse :: String *World -> (SokobanPuzzle, *World)
+parse fp w
+# (ok, f, w) = fopen fp FReadText w
+| not ok = abort ("Couldn't open file: '" +++ fp +++ "'")
+# (contents, f) = readEntireFile f
+| isEmpty contents = abort "File is empty or unreadable"
+# (ok, w) = fclose f w
+| not ok = abort "Couldn't close file"
+= (Sokoban (parseRows contents), w)
+
+parseRows :: [Char] -> [[SokobanTile]]
+parseRows cs = case parseRow cs of
+ ([], _) = []
+ (x, rest) = [x:parseRows rest]
+
+parseRow :: [Char] -> ([SokobanTile], [Char])
+parseRow [] = ([], [])
+parseRow ['\n':xs] = ([], xs)
+parseRow [x:xs]
+# (rest, xs) = parseRow xs
+# current = case x of
+ '#' = Wall
+ '$' = Box
+ '@' = Agent
+ '.' = Target
+ ' ' = Free
+ _ = abort ("Unknown char: '" +++ toString x +++ "'")
+= ([current:rest], xs)
+
+readEntireFile :: *File -> *([Char], *File)
+readEntireFile f
+# (b, c, f) = freadc f
+| not b = ([], f)
+# (cs, f) = readEntireFile f
+= ([c:cs], f)