summaryrefslogtreecommitdiff
path: root/3-kyu/sudoku.hs
blob: a302eaaf70fad552af629e68e49a0b55ef20f836 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
-- https://www.codewars.com/kata/5296bc77afba8baa690002d7
module Sudoku where

import Data.List (find, transpose)

sudoku :: [[Int]] -> [[Int]]
sudoku input = stripMaybe $ fmap (chunksOf 9) $ solveSudoku $ foldl1 (++) $ input where
  stripMaybe Nothing = []
  stripMaybe (Just x) = x
  

chunksOf :: Int -> [a] -> [[a]]
chunksOf n lst = go lst [] where
  go [] ret = reverse ret
  go rem ret = go (drop n rem) ((take n rem):ret)

type ListZipper a = ([a], [a])

moveForward :: ListZipper a -> Maybe (ListZipper a)
moveForward (_, []) = Nothing
moveForward (before, (x:xs)) = Just ((x:before), xs)

toList :: ListZipper a -> [a]
toList (before, after) = reverse before ++ after

fromList :: [a] -> ListZipper a
fromList lst = ([], lst)

zipperModify :: a -> ListZipper a -> Maybe (ListZipper a)
zipperModify _ (b, []) = Nothing
zipperModify new (before, (x:xs)) = Just (before, (new:xs))

zipperView :: ListZipper a -> Maybe a
zipperView (_, []) = Nothing
zipperView (_, (x:_)) = Just x

firstJust :: [Maybe a] -> Maybe a
firstJust lst = getResult $ find isJust lst where
    getResult (Just (Just x)) = Just x
    getResult _ = Nothing


isJust :: Maybe a -> Bool
isJust (Just _) = True
isJust Nothing  = False

solveSudoku :: [Int] -> Maybe [Int]
solveSudoku s = go $ fromList s where
  go z =
    if not $ maybeValidSudoku $ toList z then Nothing
    else
      if moveForward z == Nothing then
          Just $ toList z
      else
        if zipperView z /= Just 0 then moveForward z >>= go
        else
          firstJust $ map tryWith [1,2,3,4,5,6,7,8,9] where
           tryWith x = zipperModify x z >>= moveForward >>= go
        
maybeValidSudoku :: [Int] -> Bool
maybeValidSudoku x = all validLine (rows x ++ cols x ++ blocks x) where
  validLine line = all isUnique [1,2,3,4,5,6,7,8,9] where
    isUnique x = (length $ filter (==x) line) <= 1

rows :: [Int] -> [[Int]]
rows lst = chunksOf 9 lst

cols :: [Int] -> [[Int]]
cols lst = transpose $ chunksOf 9 lst

blocks :: [Int] -> [[Int]]
blocks lst = map block ([(rows, cols) | rows <- ranges, cols <- ranges]) where
  ranges = [(0,2), (3,5), (6,8)]
  block ((startx, endx), (starty, endy)) =
    foldl1 (++)
    $ colsRange startx endx
    $ rowsRange starty endy
    $ chunksOf 9 lst where
      rowsRange start end mat = drop start $ take (end+1) mat
      colsRange start end mat = transpose $ drop start $ take (end+1) $ transpose mat