Day 17 - added implementation using UArray but it doesn't work :(

This commit is contained in:
Jonathan Chan 2018-12-18 15:22:11 -08:00
parent 4d6f80a4db
commit 1bb2027bd0
2 changed files with 119 additions and 1 deletions

View File

@ -6,7 +6,6 @@ import Prelude hiding (lookup)
import Data.Foldable (maximumBy, minimumBy) import Data.Foldable (maximumBy, minimumBy)
import Data.Ord (comparing) import Data.Ord (comparing)
import Data.Map (Map, fromList, insert, lookup, filterWithKey, keys, size) import Data.Map (Map, fromList, insert, lookup, filterWithKey, keys, size)
import Debug.Trace (traceShow, traceShowId)
-- Horz (left, right) y | Vert x (top, bottom) -- Horz (left, right) y | Vert x (top, bottom)
data Vein = Horz (Int, Int) Int | Vert Int (Int, Int) data Vein = Horz (Int, Int) Int | Vert Int (Int, Int)

119
src/Day17_UArray.hs Normal file
View File

@ -0,0 +1,119 @@
{-# LANGUAGE TupleSections #-}
module Day17 (main) where
import Prelude hiding (lookup)
import Data.Foldable (maximumBy, minimumBy)
import Data.Ord (comparing)
import Data.Ix (range)
import Data.Array.Unboxed (UArray, array, (//), (!), elems, assocs)
-- Horz (left, right) y | Vert x (top, bottom)
type Grid = UArray Coordinate Char
type Coordinate = (Int, Int)
parse :: String -> [Coordinate]
parse str =
let fstAxis:'=':valString = takeWhile (/= ',') str
sndAxis:'=':rangeString = reverse . takeWhile (/= ' ') . reverse $ str
val = read valString
(min, max) = (read $ (takeWhile (/= '.') rangeString), (read . reverse . takeWhile (/= '.') . reverse $ rangeString))
in if fstAxis == 'x' then map (val,) [min..max] else map (,val) [min..max]
-- returns minimum y, maximum y, and grid
createGrid :: [Coordinate] -> (Int, Int, Grid)
createGrid veins =
let minX = fst $ minimumBy (comparing fst) veins
maxX = fst $ maximumBy (comparing fst) veins
minY = snd $ minimumBy (comparing snd) veins
maxY = snd $ maximumBy (comparing snd) veins
baseGrid = array ((minX - 2, 0), (maxX + 2, maxY + 1)) . map (,'.') . range $ ((minX - 2, 0), (maxX + 2, maxY + 1))
grid = baseGrid // (((500, 0), '|') : map (,'#') veins)
in (minY, maxY, grid)
flowDown :: Int -> Coordinate -> Grid -> Grid
flowDown maxY (x, y) grid = if y >= maxY then grid else
case grid ! (x, y + 1) of
'.' -> flowDown maxY (x, y + 1) $ grid // [((x, y + 1), '|')]
_ -> grid
canFlowDown :: Int -> Coordinate -> Grid -> Bool
canFlowDown maxY (x, y) grid = y < maxY && '.' == grid ! (x, y + 1)
-- Bool is True if water hits a wall and is over a floor, and False otherwise
flowLeft :: Coordinate -> Grid -> (Grid, Bool)
flowLeft (x, y) grid = case grid ! (x - 1, y) of
'#' -> (grid, True)
_ -> case grid ! (x - 1, y + 1) of
'#' -> flowLeft (x - 1, y) $ grid // [((x - 1, y), '|')]
'~' -> flowLeft (x - 1, y) $ grid // [((x - 1, y), '|')]
_ -> (grid // [((x - 1, y), '|')], False)
-- Bool is True if water hits a wall and is over a floor, and False otherwise
flowRight :: Coordinate -> Grid -> (Grid, Bool)
flowRight (x, y) grid = case grid ! (x + 1, y) of
'#' -> (grid, True)
_ -> case grid ! (x + 1, y + 1) of
'#' -> flowLeft (x + 1, y) $ grid // [((x + 1, y), '|')]
'~' -> flowLeft (x + 1, y) $ grid // [((x + 1, y), '|')]
_ -> (grid // [((x + 1, y), '|')], False)
floodLeft :: Coordinate -> Grid -> Grid
floodLeft (x, y) grid = case grid ! (x - 1, y) of
'#' -> grid
_ -> floodLeft (x - 1, y) $ grid // [((x - 1, y), '~')]
floodRight :: Coordinate -> Grid -> Grid
floodRight (x, y) grid = case grid ! (x + 1, y) of
'#' -> grid
_ -> floodLeft (x + 1, y) $ grid // [((x + 1, y), '~')]
flood :: Coordinate -> Grid -> Grid
flood coord grid =
let (lGrid, lWall) = flowLeft coord grid
(rGrid, rWall) = flowRight coord lGrid
in if lWall && rWall then floodLeft coord . floodRight coord $ rGrid // [(coord, '~')] else rGrid
canFlood :: Coordinate -> Grid -> Bool
canFlood (x, y) grid =
let validSides = (grid ! (x - 1, y), grid ! (x + 1, y)) `notElem` [('#', '|'), ('|', '#'), ('|', '|')]
validBottom = grid ! (x, y + 1) `elem` ['#', '~']
in validSides && validBottom
{-
A few edge cases:
* Flowing water with more flowing water on one side (. | | or | | .) could flow into the gap
. | | | | |
. # | => | # |
. # | | # |
* Flowing water surrounded by walls should become stagnant
# | # # ~ #
# | # => # ~ #
# # # # # #
* But flowing water between a wall and more flowing water never changes
and neither does flowing water between more flowing water
# | | |
# # # |
-}
floodFlow :: Int -> Grid -> Grid
floodFlow maxY grid =
let floodable = fst . unzip . filter (\(k, v) -> v == '|' && canFlood k grid) . assocs $ grid
flooded = foldr flood grid floodable
flowable = fst . unzip . filter (\(k, v) -> v == '|' && canFlowDown maxY k flooded) . assocs $ grid
flowed = foldr (flowDown maxY) flooded flowable
in if null flowable && null floodable then grid else floodFlow maxY flowed
part1 :: Int -> Int -> Grid -> Int
part1 minY maxY grid = length . filter (\((_, y), v) -> y >= minY && y <= maxY && (v == '|' || v == '~')) . assocs . floodFlow maxY $ grid
part2 :: Int -> Int -> Grid -> Int
part2 minY maxY grid = length . filter (\((_, y), v) -> y >= minY && y <= maxY && v == '~') . assocs . floodFlow maxY $ grid
main :: IO ()
main = do
input <- readFile "input/17.txt"
let (minY, maxY, grid) = createGrid . concat . map parse . lines $ input
print $ (minY, maxY)
print $ part1 minY maxY grid
print $ part2 minY maxY grid