Advertisement
Guest User

Untitled

a guest
Nov 22nd, 2014
168
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.91 KB | None | 0 0
  1. {-# LANGUAGE CPP #-}
  2. {-# LANGUAGE Safe #-}
  3. module Exercise5 where
  4.  
  5. import System.Random
  6. import System.IO
  7. import Data.Char
  8.  
  9.  
  10. {-
  11.  
  12. This exercise will run over several weeks, with three deadlines:
  13.  
  14. Part #1. Tuesday 11th November, 23.59.
  15. Part #2. Friday 21th November, 23.59.
  16. Part #3. Thursday 4th December, 23.59.
  17.  
  18. Testbench releases:
  19.  
  20. Part #1. Friday 7th November noon.
  21. Part #2. Friday 14th November noon.
  22. Part #3. Friday 21th November, or before.
  23.  
  24. This is the last programming exercise for the module.
  25.  
  26. For each deadline, submit this file with the solutions completed
  27. up to the required point (or more) on canvas.
  28.  
  29. We will play "peg solitaire"
  30. https://en.wikipedia.org/wiki/Peg_solitaire
  31.  
  32. You might find this website useful for testing:
  33.  
  34. http://www.pegu.it/
  35.  
  36. Before stating the exercise, we will give a few definitions.
  37.  
  38. -}
  39.  
  40. data Direction = N | E | S | W
  41. deriving (Eq, Show, Read)
  42.  
  43. -- Cartesian coordinate system
  44. -- (as used in Java 2D API)
  45. -- (x,y) :
  46. -- the x coordinate increases to the right, and
  47. -- the y coordinate increases downward,
  48. -- as shown in the following figure
  49. --
  50. -- a b c d
  51. -- 0 1 2 3 ----> x
  52. -- a 0
  53. -- b 1
  54. -- c 2
  55. -- |
  56. -- V
  57. -- y
  58. type Coord = (Int, Int)
  59.  
  60. -- A move is a coordinate and a direction.
  61. type Move = (Coord, Direction)
  62.  
  63. -- Exercise (40 points). For deadline #1.
  64. --
  65. -- Define the following function to parse a string and produce a
  66. -- sequence of moves in the above format.
  67. --
  68. --
  69. -- An example of a string of moves is "adENba2EEWS".
  70. --
  71. -- This should produce
  72. -- [((0,3),E),((2,3),N),((1,0),E),((3,0),E),((5,0),E),((7,0),W),((5,0),S)]
  73. --
  74. -- Two consecutive lower case letters are coordinates, as above, to
  75. -- move the current position to.
  76. --
  77. -- The letters N, E, W, S are directions to move to.
  78. --
  79. -- A non-negative number specifies how many times the next
  80. -- move N, E, W, or S should be performed.
  81. --
  82. -- So, in the above example,
  83. -- which contains "2EE", the move E will be performed 2+1 times.
  84. --
  85. -- Ignore all spaces (newline, tab, blank) using the function isSpace
  86. -- from the imported module Data.Char.
  87.  
  88. parseMoves :: String -> [Move]
  89. parseMoves [] = []
  90. parseMoves (' ':tl) = parseMoves(removeAllSpace tl)
  91. parseMoves (x:y:z:tl) | (isLower x) && (isLower y) && (isUpper z) = (((alphaToCoord x), (alphaToCoord y)), checkDirection z) : parseMoveHelper (makeMove (alphaToCoord x, alphaToCoord y) (checkDirection z), tl)
  92. | (isLower x) && (isLower y) && (isDigit z) = if(z/='0') then (((alphaToCoord x), (alphaToCoord y)), checkDirection c) : parseMoveHelper (makeMove (alphaToCoord x, alphaToCoord y) (checkDirection c), (convertChar (z:a) c ++ d)) else (parseMoveHelper ((alphaToCoord x, alphaToCoord y), d))
  93. | otherwise = error(x:y:z:"Illegal move")
  94. where (a, xs) = span (isDigit) tl
  95. c:d = xs
  96.  
  97. parseMoveHelper :: (Coord, String) -> [Move]
  98. parseMoveHelper (x,[]) = []
  99. parseMoveHelper (q, (x:y:z:tl)) | (isLower x) && (isLower y) && (isUpper z) = (((alphaToCoord x), (alphaToCoord y)), checkDirection z) : parseMoveHelper (makeMove (alphaToCoord x, alphaToCoord y) (checkDirection z), tl)
  100. | (isDigit x) && (isDigit y) && (isDigit z) = if(x/='0') then (q, checkDirection c) : parseMoveHelper (makeMove q (checkDirection c), ((convertChar (x:y:z:a) c) ++ tl)) else (parseMoveHelper (q, d))
  101. | (isDigit x) && (isDigit y) && (isUpper z) = if(x/='0') then (q, checkDirection z) : parseMoveHelper (makeMove q (checkDirection z), ((convertChar (x:y:[]) z) ++ tl)) else (parseMoveHelper (q, tl))
  102. | (isLower x) && (isLower y) && (isDigit z) = if(z/='0') then (((alphaToCoord x), (alphaToCoord y)), checkDirection c) : parseMoveHelper (makeMove (alphaToCoord x, alphaToCoord y) (checkDirection c), (convertChar (z:a) c ++ d)) else (parseMoveHelper ((alphaToCoord x, alphaToCoord y), d))
  103. | (isUpper x) = (q, checkDirection x) : parseMoveHelper (makeMove q (checkDirection x), (y:z:tl))
  104. | (isDigit x) && (isUpper y) = if(x/='0') then (q, checkDirection y) : parseMoveHelper (makeMove q (checkDirection y), ((convertChar (x:[]) y) ++ z:tl)) else (parseMoveHelper (q, (z:tl)))
  105. where (a, xs) = span (isDigit) tl
  106. c:d = xs
  107. parseMoveHelper (q, (x:y:tl)) | (isDigit x) = if(x/='0') then (q, checkDirection y) : parseMoveHelper (makeMove q (checkDirection y), ((convertChar (x:[]) y)) ++ tl) else (parseMoveHelper (q, tl))
  108. | (isUpper x) = (q, checkDirection x) : parseMoveHelper (makeMove q (checkDirection x), (y:tl))
  109. parseMoveHelper (q, (x:tl)) = (q, checkDirection x) : parseMoves tl
  110.  
  111. -- this assigns the upper case alphabets to directions.
  112. checkDirection :: Char -> Direction
  113. checkDirection x | x=='N' = N
  114. | x=='S' = S
  115. | x=='E' = E
  116. | x=='W' = W
  117.  
  118. -- this will turns the character to the an coord int.
  119. alphaToCoord :: Char -> Int
  120. alphaToCoord hd = (ord hd) - 97
  121.  
  122. -- this will make the move according to the direction after the direction has been checked
  123. makeMove :: Coord -> Direction -> Coord
  124. makeMove (x, y) d | d==N = (x, y-2)
  125. | d==S = (x, y+2)
  126. | d==E = (x+2, y)
  127. | d==W = (x-2, y)
  128.  
  129. convertChar :: String -> Char -> String
  130. convertChar a b = if (a/= "0") then (replicate (((read (a)::Int)) - 1) b) else []
  131.  
  132. removeAllSpace :: String -> String
  133. removeAllSpace [] = []
  134. removeAllSpace (x:xs)
  135. | (isSpace x) = (removeAllSpace xs)
  136. | otherwise = x:(removeAllSpace xs)
  137.  
  138.  
  139.  
  140. -- We need the following definition for the next exercise.
  141. --
  142. -- The state of each place in the board is one of the following:
  143. --
  144. -- - illegal
  145. -- - empty
  146. -- - occupied
  147. --
  148. -- So, a legal position is one which is empty or occupied.
  149. --
  150. -- When (i,j) is illegal then no ((i,j),_) is in the list defining the
  151. -- state of the board.
  152. --
  153. -- ((i,j),True) means (i,j) is occupied by a peg, and
  154. -- ((i,j),False) means (i,j) is empty,
  155. -- where 0 <= i,j <= 25, i.e. the maximum size of a board is 26x26
  156. type BoardSpec = [(Coord, Bool)]
  157.  
  158. -- Exercise (10 points). For deadline #1.
  159. --
  160. -- Write a function that counts how many pegs
  161. -- are in a given board:
  162. countPeg :: BoardSpec -> Int
  163. countPeg [] = 0
  164. countPeg ((a,b) : xs)
  165. | b == True = 1 + countPeg xs
  166. | b == False = 0 + countPeg xs
  167.  
  168. -- Exercise (40 points). For deadline #1.
  169. --
  170. -- Convert a board to a String.
  171. --
  172. -- An example of a output is
  173. --
  174. -- o o o
  175. -- o o o
  176. -- o o o o o o o
  177. -- o o o - o o o
  178. -- o o o o o o o
  179. -- o o o
  180. -- o o o
  181. --
  182. -- There is a space between each pair of adjacent cells in the same line.
  183. -- An illegal cell is presented by a space if there are legal cells on the right.
  184. -- 'o' means that the cell is occupied by a peg, and
  185. -- '-' means that the cell is empty.
  186. -- Notice that there is NO spaces in the end of each line.
  187. --
  188. -- This will be useful for you to test your program.
  189.  
  190. highestX :: Int -> Int -> BoardSpec -> Int
  191. highestX i a [] = i
  192. highestX i a (((x, y), b): xs) = if ((i < x) && (a == y)) then highestX x a xs else highestX i a xs
  193.  
  194.  
  195. highestY :: Int -> BoardSpec -> Int
  196. highestY i [] = i
  197. highestY i (((x,y),b):xs) = if i < y then highestY y xs else highestY i xs
  198.  
  199. showBoardHelper1 :: Coord -> BoardSpec -> String
  200. showBoardHelper1 a [] = []
  201. showBoardHelper1 (a, b) xs
  202. |elem ((a, b), True) xs = if (a < (highestX (-1) b xs)) then "o " ++ showBoardHelper1 (a + 1, b) xs else "o" ++ showBoardHelper2 (0, b + 1) xs
  203. |elem ((a, b), False) xs = if (a < (highestX (-1) b xs)) then "- " ++ showBoardHelper1 (a + 1, b) xs else "-" ++ showBoardHelper2 (0, b + 1) xs
  204. |otherwise = if (a < highestX (-1) b xs) then " " ++ showBoardHelper1 (a + 1, b) xs else " " ++ showBoardHelper2 (0, b + 1) xs
  205.  
  206. showBoardHelper2 :: Coord -> BoardSpec -> String
  207. showBoardHelper2 a [] = []
  208. showBoardHelper2 (a, b) xs
  209. |b > (highestY (-1) xs) = []
  210. |elem ((a, b), True) xs = if (a < highestX (-1) b xs) then "\no " ++ showBoardHelper1 (a + 1, b) xs else "\no" ++ showBoardHelper2 (0, b + 1) xs
  211. |elem ((a, b), False) xs = if (a < highestX (-1) b xs) then "\n- " ++ showBoardHelper1 (a + 1, b) xs else "\n-" ++ showBoardHelper2 (0, b + 1) xs
  212. |otherwise = if (a < highestX (-1) b xs) then "\n " ++ showBoardHelper1 (a + 1, b) xs else "\n " ++ showBoardHelper2 (0, b + 1) xs
  213.  
  214. showBoard :: BoardSpec -> String
  215. showBoard all@(x:xs) = showBoardHelper1 (0, 0) (all)
  216.  
  217. -- This too:
  218. printBoard :: BoardSpec -> IO ()
  219. printBoard = putStrLn . showBoard
  220.  
  221.  
  222. -- Exercise (40 points). For deadline #1.
  223. --
  224. -- Given a board specification (of type BoardSpec defined below), and
  225. -- given a list of moves (of type Move defined above), produce either
  226. -- Nothing (when a move in the list is impossible), or Just the
  227. -- resulting board after performing all the moves.
  228. --
  229. -- Using this, and the function countPeg you defined above, also
  230. -- define a function simulateMoves which only gives Just the number of
  231. -- resulting pegs, or Nothing if an illegal move occurs:
  232.  
  233. runMoves :: BoardSpec -> [Move] -> Maybe BoardSpec
  234. runMoves [] a = Nothing
  235. runMoves xs [] = Just xs
  236. runMoves xs (((a,b), d):tl) = runMoves (firstMove ((a, b), d) xs) tl
  237.  
  238. firstMove :: Move -> BoardSpec -> BoardSpec
  239. firstMove ((e, f), d) xs = case d of
  240. N -> if c then secondMove ((e, f - 1), d) (init as ++ ((a, b), False):[] ++ bs) else []
  241. E -> if c then secondMove ((e + 1, f), d) (init as ++ ((a, b), False):[] ++ bs) else []
  242. S -> if c then secondMove ((e, f + 1), d) (init as ++ ((a, b), False):[] ++ bs) else []
  243. W -> if c then secondMove ((e - 1, f), d) (init as ++ ((a, b), False):[] ++ bs) else []
  244. where (as, bs) = if (findCoord (e, f) xs) > 0 then splitAt (findCoord (e, f) xs) xs else (xs, [])
  245. ((a, b), c) = last as
  246.  
  247. secondMove :: Move -> BoardSpec -> BoardSpec
  248. secondMove ((e, f), d) xs = case d of
  249. N -> if c then thirdMove ((e, f - 1), d) (init as ++ ((a, b), False):[] ++ bs) else []
  250. E -> if c then thirdMove ((e + 1, f), d) (init as ++ ((a, b), False):[] ++ bs) else []
  251. S -> if c then thirdMove ((e, f + 1), d) (init as ++ ((a, b), False):[] ++ bs) else []
  252. W -> if c then thirdMove ((e - 1, f), d) (init as ++ ((a, b), False):[] ++ bs) else []
  253. where (as, bs) = if (findCoord (e, f) xs) > 0 then splitAt (findCoord (e, f) xs) xs else (xs, [])
  254. ((a, b), c) = last as
  255.  
  256. thirdMove :: Move -> BoardSpec -> BoardSpec
  257. thirdMove ((e, f), d) xs = if (not c) then (init as ++ ((a, b), True):[] ++ bs) else []
  258. where (as, bs) = if (findCoord (e, f) xs) > 0 then splitAt (findCoord (e, f) xs) xs else (xs, [])
  259. ((a, b), c) = last as
  260.  
  261. findCoord :: Coord -> BoardSpec -> Int
  262. findCoord (e, f) [] = (-999999999999)
  263. findCoord (e, f) (((x, y), b):xs) = if (e==x) && (f==y) then 1 else 1 + (findCoord (e, f) xs)
  264.  
  265. maybe2Board :: Maybe BoardSpec -> BoardSpec
  266. maybe2Board Nothing = error("Nothing")
  267. maybe2Board (Just xs) = xs
  268.  
  269. simulateMoves :: BoardSpec -> [Move] -> Maybe Int
  270. simulateMoves xs ys = if (runOutput == Nothing) then Nothing else (Just count)
  271. where
  272. runOutput = runMoves xs ys
  273. maybeOut = maybe2Board runOutput
  274. count = countPeg maybeOut
  275.  
  276. -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
  277.  
  278. -- Exercise (40 points). For deadline #2
  279. --
  280. -- Two positions of a board are adjacent if (1) they are next to each
  281. -- other vertically, or (2) they are next to each other
  282. -- horizontally. A board is called connected if every legal position
  283. -- may be reached from any other legal position by travelling via
  284. -- adjacent legal positions.
  285. --
  286. -- Check whether (the legal positions of) a board are connected.
  287.  
  288. {- >>>>>>>>> DON'T NEED TO USE IT
  289.  
  290. sortBoardHelper1 :: Coord -> BoardSpec -> BoardSpec
  291. sortBoardHelper1 a [] = []
  292. sortBoardHelper1 (a,b) xs
  293. |elem ((a, b), True) xs = if (a < (highestX (-1) b xs)) then [((a,b), True)] ++ sortBoardHelper1 (a + 1, b) xs else [((a,b), True)] ++ sortBoardHelper2 (0, b + 1) xs
  294. |elem ((a, b), False) xs = if (a < (highestX (-1) b xs)) then [((a,b), False)] ++ sortBoardHelper1 (a + 1, b) xs else [((a,b), False)] ++ sortBoardHelper2 (0, b + 1) xs
  295. |otherwise = if (a < highestX (-1) b xs) then [] ++ sortBoardHelper1 (a + 1, b) xs else [] ++ sortBoardHelper2 (0, b + 1) xs
  296.  
  297. sortBoardHelper2 :: Coord -> BoardSpec -> BoardSpec
  298. sortBoardHelper2 a [] = []
  299. sortBoardHelper2 (a, b) xs
  300. |b > (highestY (-1) xs) = []
  301. |elem ((a, b), True) xs = if (a < highestX (-1) b xs) then [((a,b), True)] ++ sortBoardHelper1 (a + 1, b) xs else [((a,b), True)] ++ sortBoardHelper2 (0, b + 1) xs
  302. |elem ((a, b), False) xs = if (a < highestX (-1) b xs) then [((a,b), False)] ++ sortBoardHelper1 (a + 1, b) xs else [((a,b), False)] ++ sortBoardHelper2 (0, b + 1) xs
  303. |otherwise = if (a < highestX (-1) b xs) then [] ++ sortBoardHelper1 (a + 1, b) xs else [] ++ sortBoardHelper2 (0, b + 1) xs
  304.  
  305.  
  306. sortBoard :: BoardSpec -> BoardSpec
  307. sortBoard all = sortBoardHelper1 (0, 0) (all)
  308.  
  309. -}
  310.  
  311. isConAdj :: Coord -> BoardSpec -> Bool
  312. isConAdj (x,y) [] = False
  313. isConAdj (x,y) (((x',y'),b):xs)
  314. | x' == x+1 && y' == y = True
  315. | x' == x-1 && y' == y = True
  316. | x' == x && y' == y+1 = True
  317. | x' == x && y' == y-1 = True
  318. | otherwise = False || isConAdj (x,y) xs
  319.  
  320.  
  321. isConHelper :: BoardSpec -> BoardSpec -> BoardSpec -> BoardSpec
  322. isConHelper ys [] visited = visited
  323. isConHelper ys (((x,y),b):xs) visited = if isConAdj (x,y) ys then isConHelper ys xs (visited ++ [((x,y),b)]) else isConHelper ys xs visited
  324.  
  325. isConnected :: BoardSpec -> Bool
  326. isConnected xs
  327. | tail(xs) == [] = True
  328. | otherwise = xs == isConHelper xs xs []
  329.  
  330.  
  331.  
  332. -- Bonus Exercise (20 points). For deadline #2
  333. --
  334. -- Count how many connected components the legal positions form.
  335. --
  336. -- There is at most one connected component if the board is connected
  337. -- as above.
  338. numConnectedComponents :: BoardSpec -> Int
  339. numConnectedComponents = undefined
  340.  
  341. -- We define our own Rand monad for random number generation:
  342. newtype Rand a = Rand(StdGen -> (a , StdGen))
  343.  
  344. instance Monad Rand where
  345. return x = Rand (\s -> (x,s))
  346. Rand g >>= f = Rand (\s -> let (x, s') = g s
  347. (Rand h) = f x
  348. in h s')
  349.  
  350. randDouble :: Rand Double
  351. randDouble = Rand random
  352.  
  353. randInt :: Rand Int
  354. randInt = Rand random
  355.  
  356. -- Run a Rand with the specified initial seed
  357. runRand :: Int -> Rand a -> a
  358. runRand seed (Rand g) = fst (g (mkStdGen seed))
  359.  
  360.  
  361. -- Generates a random element ---cxx993@cs.bham.ac.uk
  362. uniform :: [a] -> Rand a
  363. uniform l = do
  364. i <- randInt
  365. let n = abs i `mod` (length l)
  366. return $ l !! n
  367.  
  368. -- Example:
  369. --
  370. -- *Template6 Control.Monad> runRand 1 (replicateM 30 (uniform [1..10]))
  371. -- [5,2,1,1,10,8,5,7,2,8,7,2,6,3,5,7,1,10,3,6,6,1,9,2,7,8,3,10,6,4]
  372. --
  373. -- (This in principle can give different results in different versions of Haskell,
  374. -- if the random generation procedure in the libraries are changed.)
  375.  
  376. testRand :: Rand (Int, Int, Int)
  377. testRand = do
  378. x <- uniform [1..10]
  379. y <- uniform [1..10]
  380. z <- uniform [1..10]
  381. return (x, y, z)
  382.  
  383. -- Exercise (30 points). For deadline #2.
  384. -- Generate a random square board of a certain height and width.
  385.  
  386. randBool :: Rand Bool
  387. randBool = Rand random
  388.  
  389.  
  390.  
  391.  
  392. genBool :: IO Bool
  393. genBool = getStdRandom (randomR (True,False))
  394.  
  395. io2Bool :: IO Bool -> Bool
  396. io2Bool i =
  397.  
  398.  
  399.  
  400. genCoord :: Int -> Int -> Int -> [Coord] -> [Coord]
  401. genCoord 0 x y coords = []
  402. genCoord i x y coords
  403. | x < i-1 = genCoord i (x+1) y cod
  404. | y < i-1 = genCoord i 0 (y+1) cod
  405. | otherwise = cod
  406. where cod = coords ++ [(x,y)]
  407.  
  408.  
  409. populate :: [Coord] -> BoardSpec -> BoardSpec
  410. populate [] lastfil = lastfil
  411. populate (x:xs) filled = filled ++ [(x,y)] ++ populate xs filled
  412. where IO y = genBool
  413.  
  414. genBoardSpec :: Int -> Rand BoardSpec
  415. genBoardSpec = undefined
  416.  
  417. {-genBoardSpec i = return (populate (genCoord i) [])
  418.  
  419. genBoardSpec x = do
  420. n <- Rand True -- n == True
  421. -}
  422.  
  423. -- Exercise (30 points). For deadline #2.
  424. --
  425. -- Given a board, randomly play legal moves until no further legal
  426. -- moves are possible.
  427.  
  428. playRandomly :: BoardSpec -> Rand [Move]
  429. playRandomly = undefined
  430.  
  431.  
  432. -- Exercise (100 points). For deadline #3.
  433. --
  434. -- This will be your best try for playing the game intelligently.
  435. --
  436. -- To get any mark at all in this exercise, your solution should (1)
  437. -- take no more than the test bench allows, in a lab machine, in a
  438. -- board size given by the test bench, and (2) be strictly better than
  439. -- a random play.
  440. --
  441. -- The test bench for this part will be given in advance.
  442. --
  443. --
  444. -- Mark allocation, subject to the above restrictions.
  445. --
  446. -- 100 for ending up with one peg, and fast (according to the testbench).
  447. -- 70 for ending up with one peg, and reasonably fast (according to the testbench).
  448. -- 40-69 for ending with few pegs, and reasonably fast (according to the testbench).
  449. -- 1-39 see test bench results.
  450. --
  451. -- The test bench will only give you approximate marks, due to the
  452. -- probabilistic nature of our testing. We will test your program
  453. -- several times and take averages.
  454.  
  455. play :: BoardSpec -> [Move]
  456. play = undefined
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement