aboutsummaryrefslogtreecommitdiff
path: root/2020/20a.hs
diff options
context:
space:
mode:
Diffstat (limited to '2020/20a.hs')
-rw-r--r--2020/20a.hs92
1 files changed, 92 insertions, 0 deletions
diff --git a/2020/20a.hs b/2020/20a.hs
new file mode 100644
index 0000000..cb91d54
--- /dev/null
+++ b/2020/20a.hs
@@ -0,0 +1,92 @@
+{-# LANGUAGE TupleSections #-}
+
+module Day20A where
+
+import System.Environment (getArgs)
+import Data.List.Split (splitOn)
+import Data.List.Extra (groupOn)
+import Data.List (transpose, sort, sortOn)
+import Data.Bool (bool)
+
+-- Types
+
+-- (y, x)
+type Tile = [[Bool]];
+type TileWithId = (Int, Tile);
+type Coord = (Int, Int);
+
+-- Flip Vertical, Rotations (0-3)
+data Orientation = Transform Bool Int;
+
+orients :: [Orientation]
+orients = [Transform f r | f <- [True, False], r <- [0..3]]
+
+-- Tile manipulation
+rotate :: Tile -> Tile
+rotate = transpose . reverse
+
+rotateN :: Int -> Tile -> Tile
+rotateN 0 t = t
+rotateN n t = rotateN (n - 1) (rotate t)
+
+reorient :: Orientation -> Tile -> Tile
+reorient (Transform False n) = rotateN n
+reorient (Transform True n) = rotateN n . reverse
+
+-- Edge manipulation
+
+-- A number uniquely representing this edge
+edgeIdentifier :: [Bool] -> Int
+edgeIdentifier = foldl (\n s -> n * 2 + bool 0 1 s) 0
+
+-- All edges that this tile can have
+-- Returns list of edge ids
+allEdges :: Tile -> [Int]
+allEdges t = map (edgeIdentifier . head . (`reorient` t)) orients
+
+getCornerIDs :: [TileWithId] -> [Int]
+getCornerIDs ts = map (fst . head) $ filter ((== 4) . length) tilesWithUniqEdges
+ where edges = concatMap (\(i, t) -> map (i,) $ allEdges t) ts
+ uniqEdges = map head $ filter ((== 1) . length) $ groupOn snd $ sortOn snd edges
+ tilesWithUniqEdges = groupOn fst $ sort uniqEdges
+
+-- runghc --ghc-arg='-package split' --ghc-arg='-package extra' 20a.hs inputs/day20
+main :: IO ()
+main = do
+ args <- getArgs;
+ ts <- parseFile (head args);
+
+ print $ product $ getCornerIDs ts;
+ return ();
+
+-- Utilities
+maybeNoDefault :: (a -> b) -> Maybe a -> Maybe b
+maybeNoDefault _ Nothing = Nothing
+maybeNoDefault f (Just x) = Just (f x)
+
+unwrap :: Maybe a -> a
+unwrap (Just x) = x
+unwrap _ = error "unwrap on null value"
+
+-- Get all possible combinations by picking one element from each of the sublists
+oneFromEach :: [[a]] -> [[a]]
+oneFromEach [] = [[]]
+oneFromEach (xs:xss) = concat $ [ map (x : ) (oneFromEach xss) | x <- xs]
+
+-- Parsing
+parseTileDef :: String -> (Int, Tile)
+parseTileDef s = (read n, map readLine ls)
+ where (f:ls) = lines s
+ n = drop 5 $ take (length f - 1) f
+ readLine [] = []
+ readLine ('.':xs) = False : readLine xs
+ readLine ('#':xs) = True : readLine xs
+ readLine _ = error "invalid input"
+
+parseInput :: String -> [(Int, Tile)]
+parseInput = map parseTileDef . splitOn "\n\n"
+
+parseFile :: String -> IO [(Int, Tile)]
+parseFile f = do
+ c <- readFile f
+ return $ parseInput c; \ No newline at end of file