[hs] Solve 2020_20 part 1

This commit is contained in:
Joscha 2020-12-20 13:21:37 +00:00
parent bd7575a9f5
commit 6b3bcb35b3
2 changed files with 122 additions and 0 deletions

View file

@ -22,6 +22,7 @@ import qualified Aoc.Y2020.D16 as D16
import qualified Aoc.Y2020.D17 as D17
import qualified Aoc.Y2020.D18 as D18
import qualified Aoc.Y2020.D19 as D19
import qualified Aoc.Y2020.D20 as D20
year :: Year
year = Year 2020
@ -44,4 +45,5 @@ year = Year 2020
, (17, D17.day)
, (18, D18.day)
, (19, D19.day)
, (20, D20.day)
]

120
hs/src/Aoc/Y2020/D20.hs Normal file
View file

@ -0,0 +1,120 @@
{-# LANGUAGE OverloadedStrings #-}
module Aoc.Y2020.D20
( day
) where
import Data.List
import Data.Maybe
import Control.Monad
import qualified Data.Map as Map
import qualified Data.Set as Set
import Aoc.Day
import Aoc.Parse
newtype Tile = Tile [[Bool]] -- List of rows
deriving (Show)
tLeft :: Tile -> [Bool]
tLeft (Tile l) = map head l
tRight :: Tile -> [Bool]
tRight (Tile l) = map last l
tTop :: Tile -> [Bool]
tTop (Tile l) = head l
tBottom :: Tile -> [Bool]
tBottom (Tile l) = last l
tTranspose :: Tile -> Tile
tTranspose (Tile l) = Tile $ transpose l
tFlipV :: Tile -> Tile
tFlipV (Tile l) = Tile $ reverse l
tFlipH :: Tile -> Tile
tFlipH (Tile l) = Tile $ map reverse l
tTurnCw :: Tile -> Tile
tTurnCw = tFlipH . tTranspose
tTurnCcw :: Tile -> Tile
tTurnCcw = tFlipV . tTranspose
tRotations :: Tile -> [Tile]
tRotations = take 4 . iterate tTurnCw
tVariations :: Tile -> [Tile]
tVariations t = tRotations t ++ tRotations (tFlipH t)
parser :: Parser (Map.Map Int Tile)
parser = Map.fromList <$> (tile `sepBy` newline)
where
field = (False <$ string ".") <|> (True <$ "#")
row = sequenceA (replicate 10 field) <* newline
tile = do
tid <- string "Tile " *> decimal <* string ":" <* newline
rows <- sequenceA $ replicate 10 row
pure (tid, Tile rows)
deduplicate :: (Ord a) => [a] -> [a]
deduplicate = Set.toList . Set.fromList
type Pos = (Int, Int)
vicinity :: Pos -> [Pos]
vicinity (x, y) = [(x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1)]
freeAdjacents :: Map.Map Pos a -> [Pos]
freeAdjacents m =
let adjacents = deduplicate $ concatMap vicinity $ Map.keys m
taken = Map.keysSet m
in filter (not . (`Set.member` taken)) adjacents
unplaced :: Map.Map Int Tile -> Map.Map Pos (Int, Tile) -> [Int]
unplaced tiles placed = Set.toList $ Map.keysSet tiles Set.\\ Set.fromList (map fst $ Map.elems placed)
valid :: Map.Map Pos (Int, Tile) -> Pos -> Tile -> Bool
valid placed (x, y) tile = validLeft && validRight && validTop && validBottom
where
validAt pos f g = maybe True (\a -> f (snd a) == g tile) $ placed Map.!? pos
validLeft = validAt (x - 1, y) tRight tLeft
validRight = validAt (x + 1, y) tLeft tRight
validTop = validAt (x, y - 1) tBottom tTop
validBottom = validAt (x, y + 1) tTop tBottom
place :: Map.Map Int Tile -> Map.Map Pos (Int, Tile) -> Maybe (Map.Map Pos (Int, Tile))
place tiles placed = listToMaybe $ do
tid <- unplaced tiles placed
let tile = tiles Map.! tid
pos <- freeAdjacents placed
var <- tVariations tile
guard $ valid placed pos var
pure $ Map.insert pos (tid, var) placed
whileJust :: (a -> Maybe a) -> a -> a
whileJust f a = maybe a (whileJust f) $ f a
placeAll :: Map.Map Int Tile -> Map.Map Pos (Int, Tile)
placeAll tiles = whileJust (place tiles) $ Map.singleton (0, 0) $ head $ Map.assocs tiles
corners :: [Pos] -> [Pos]
corners positions =
let minX = minimum $ map fst positions
maxX = maximum $ map fst positions
minY = minimum $ map snd positions
maxY = maximum $ map snd positions
in (,) <$> [minX, maxX] <*> [minY, maxY]
solver :: Map.Map Int Tile -> IO ()
solver tiles = do
putStrLn ">> Part 1"
let placed = placeAll tiles
cornerIds = map (fst . (placed Map.!)) $ corners $ Map.keys placed
print $ product cornerIds
day :: Day
day = dayParse parser solver