I've been working on the n queens problem on Haskell and I have been able to solve it for the most part.
queens :: Int -> [[Int]]
queens 0 = [[]]
queens n = [ x : y | y <- queens (n-1), x <- [1..8], safe x y 1]
where
safe x [] n = True
safe x (y:ys) n = and [ x /= y , x /= y + n , x /= y - n , safe x ys (n+1)]
drawQueens :: [Int] -> String
drawQueens [] = ""
drawQueens x = "1 2 3 4 5 6 7 8" ++ ['\n'] ++ concatMap showRow x ++ ['\n']
where
size = length x
spaces = replicate size '_' ++ ""
showRow n = take (n - 1) spaces ++ "D" ++ take (size - n) spaces ++ ['\n']
My second function, drawQueens, should ideally transform one solution to the queens problem to an ASCII graphic (see image below). My problems/questions are:
2.Why isn't the compiler interpreting the ['\n']? How can I modify my code in order to achieve the desired output?
This was initially a homework question during the semester but I am now doing it just for myself.
This is what my output of drawQueens looks like currently
Thank you for all your help.
The compiler “interprets” '\n'
just fine. Only, newlines aren't really “safe characters”, as in: you can't use a string literal with newlines in it directly in Haskell code. And the output of print
, which GHCi uses by default to, well, print stuff, always tries to generate valid Haskell code, hence it escapes these newlines in again. This can be surpressed if you just instruct it to cat the string as it is to the terminal:
*Main> drawQueens [4,2,7,3,6,8,5,1]
"1 2 3 4 5 6 7 8\n___D____\n_D______\n______D_\n__D_____\n_____D__\n_______D\n____D___\nD_______\n\n"
*Main> putStrLn $ drawQueens [4,2,7,3,6,8,5,1]
1 2 3 4 5 6 7 8
___D____
_D______
______D_
__D_____
_____D__
_______D
____D___
D_______
There's still another problem: you don't have the same spacing in the number ow as in the actuall chessboard. Well, that's easily fixed too.
drawQueens :: [Int] -> String
drawQueens [] = ""
drawQueens x = "1 2 3 4 5 6 7 8" ++ "\n" ++ concatMap showRow x
where
size = length x
spaces n = concat $ replicate n "□ "
showRow n = spaces (n - 1) ++ "♛ " ++ spaces (size - n) ++ "\n"
This then gives:
*Main> putStrLn $ drawQueens [4,2,7,3,6,8,5,1]
1 2 3 4 5 6 7 8
□ □ □ ♛ □ □ □ □
□ ♛ □ □ □ □ □ □
□ □ □ □ □ □ ♛ □
□ □ ♛ □ □ □ □ □
□ □ □ □ □ ♛ □ □
□ □ □ □ □ □ □ ♛
□ □ □ □ ♛ □ □ □
♛ □ □ □ □ □ □ □
Fancy version:
chessboardRow, chessboardRow' :: [Maybe Char] -> String
chessboardRow' [] = "▌"
chessboardRow' (Just c:cs) = '▌':c:chessboardRow cs
chessboardRow' (Nothing:cs) = "▌ "++chessboardRow cs
chessboardRow [] = " "
chessboardRow (Just c:cs) = '▐':c:chessboardRow' cs
chessboardRow (Nothing:cs) = "▐█"++chessboardRow' cs
drawQueens :: [Int] -> String
drawQueens [] = ""
drawQueens x = " a b c d e f g h" ++ "\n"
++ concat (reverse $
zipWith3 showRow
['1'..]
(cycle [chessboardRow, chessboardRow'])
x)
++ "\n"
where
size = length x
showRow i rsh n = i : rsh (replicate (n - 1) Nothing
++ [Just '♛']
++ replicate (size - n) Nothing)
++ "\n"
gives
a b c d e f g h
8▌♛▐█▌ ▐█▌ ▐█▌ ▐█▌
7▐█▌ ▐█▌ ▐♛▌ ▐█▌
6▌ ▐█▌ ▐█▌ ▐█▌ ▐♛▌
5▐█▌ ▐█▌ ▐█▌♛▐█▌
4▌ ▐█▌♛▐█▌ ▐█▌ ▐█▌
3▐█▌ ▐█▌ ▐█▌ ▐♛▌
2▌ ▐♛▌ ▐█▌ ▐█▌ ▐█▌
1▐█▌ ▐█▌♛▐█▌ ▐█▌