Guest User

MastermindSolver

a guest
Mar 31st, 2021
69
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. module Main where
  2. import Data.Char
  3. import qualified Data.Set as S
  4. import qualified Data.List as L
  5.  
  6. type Quad  = (Int, Int, Int, Int)
  7. type Distance = (Int, Int)
  8. type Move = (Quad, Distance, Int)
  9.  
  10. main :: IO ()
  11. main = do
  12.     code <- getLine
  13.     putStrLn (solve code)
  14.  
  15. when :: (a -> Bool) -> a -> (a -> a) -> a -> a
  16. when c r f s
  17.     | c s       = f s
  18.     | otherwise = r
  19.  
  20. solve :: String -> String
  21. solve = when l l' $ when d d' run
  22.     where l  = (==) 4 . length
  23.           l' = "Enter exactly 4 digits"
  24.          d  = and . map ((`elem` [1..6]) . digitToInt)
  25.          d' = "Only use digits 1-6"
  26.  
  27. run :: String -> String
  28. run = unlines . map moveToString . knuth . quad . map digitToInt
  29.  
  30. quad :: [Int] -> Quad
  31. quad [a, b, c, d] = (a, b, c, d)
  32.  
  33. unquad :: Quad -> [Int]
  34. unquad (a, b, c, d) = [a, b, c, d]
  35.  
  36. allQuads :: S.Set Quad
  37. allQuads = S.fromList [(x, y, z, w) | x <- [1..6], y <- [1..6], z <- [1..6], w <- [1..6]]
  38.  
  39. knuth :: Quad -> [Move]
  40. knuth = guess (1, 1, 2, 2) allQuads allQuads
  41.  
  42. -- Having leftover guesses l and possible answers s
  43. -- Test guess g and determine:
  44. --   l' (l without g)
  45. --   s' (possibilities after answer to g)
  46. --   g' the next guess based on l' and s'
  47. guess :: Quad -> S.Set Quad -> S.Set Quad -> Quad -> [Move]
  48. guess g l s q
  49.     | d == (4,0) = m:[]
  50.     | otherwise  = m:m'
  51.    where g' = S.findMin s' -- TODO: Optimize using the real algo
  52.          l' = S.delete g l
  53.           s' = S.filter (equidistant g d) s
  54.          d  = distance g q
  55.          m  = (g, d, S.size s')
  56.           m' = guess g' l' s' q
  57.  
  58. equidistant :: Quad -> Distance -> Quad -> Bool
  59. equidistant g d q = distance q g == d
  60.  
  61. split :: [Int] -> [Int] -> ([(Int, Int)], [(Int, Int)])
  62. split [] [] = ([],[])
  63. split (x:xs) (y:ys)
  64.     | x == y    = ((x,y):z, z')
  65.    | otherwise = (z, (x,y):z')
  66.     where (z, z') = split xs ys
  67.  
  68. distance :: Quad -> Quad -> Distance
  69. distance g q = (a, b)
  70.    where (x, y)   = split (unquad g) (unquad q)
  71.          a        = length x
  72.          (x', y') = unzip y
  73.          b        = weakDistance x' y'
  74.  
  75. weakDistance :: [Int] -> [Int] -> Int
  76. weakDistance [] _ = 0
  77. weakDistance (g:gs) q
  78.    | g `elem` q = n+1
  79.    | otherwise  = n
  80.    where n  = weakDistance gs q'
  81.           q' = L.delete g q
  82.  
  83. moveToString :: Move -> String
  84. moveToString (g, d, s) = "Guessed " ++ g' ++ " result " ++ d' ++ " left " ++ s' ++ " combinations"
  85.     where g' = (map intToDigit . unquad) g
  86.        d' = show d
  87.         s' = show s
Advertisement
Add Comment
Please, Sign In to add comment