Skip to content

Commit 5cce969

Browse files
committed
Rewrite p1 in terms of p2
1 parent 6f05993 commit 5cce969

File tree

2 files changed

+115
-4
lines changed

2 files changed

+115
-4
lines changed

05.hs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ parseAlmanac s = case parse almanac "" s of
2626
almanac = Almanac <$> seeds <*> maps
2727
mkRangeMapping a b c = RangeMapping (Range b c) (Range a c)
2828

29-
p1 :: Almanac -> Int
30-
p1 Almanac { seeds, maps } = minimum $ map (`mTransform` maps) seeds
29+
p1' :: Almanac -> Int
30+
p1' Almanac { seeds, maps } = minimum $ map (`mTransform` maps) seeds
3131

3232
-- Guide a seed through the transformations under the given maps
3333
mTransform :: Int -> [Map] -> Int
@@ -52,14 +52,24 @@ offsetInRange :: Range -> Int -> Maybe Int
5252
offsetInRange Range { start, len } x =
5353
if x >= start && x <= (start + len) then Just (x - start) else Nothing
5454

55+
p1 :: Almanac -> Int
56+
p1 Almanac { seeds, maps } = solve (identityRanges seeds) maps
57+
5558
p2 :: Almanac -> Int
56-
p2 Almanac { seeds, maps } = minimum . map start . filter (\r -> len r /= 0) $
57-
foldl transformRanges (seedRanges seeds) maps
59+
p2 Almanac { seeds, maps } = solve (seedRanges seeds) maps
60+
61+
identityRanges :: [Int] -> [Range]
62+
identityRanges [] = []
63+
identityRanges (x:rest) = Range x 1 : identityRanges rest
5864

5965
seedRanges :: [Int] -> [Range]
6066
seedRanges [] = []
6167
seedRanges (x:y:rest) = Range x y : seedRanges rest
6268

69+
solve :: [Range] -> [Map] -> Int
70+
solve rs maps = minimum . map start . filter (\r -> len r /= 0) $
71+
foldl transformRanges rs maps
72+
6373
-- Transform seed ranges under the given range map. This may result in more
6474
-- ranges than we started with.
6575
transformRanges :: [Range] -> Map -> [Range]

05.variations.hs

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import Text.Parsec
2+
import Control.Monad (void)
3+
4+
main :: IO ()
5+
main = interact $ (++ "\n") . show . ((,) <$> p1 <*> p2) . parseAlmanac
6+
7+
data Almanac = Almanac { seeds :: [Int], maps :: [Map] }
8+
type Map = [RangeMapping]
9+
data RangeMapping = RangeMapping { from :: Range, to :: Range }
10+
data Range = Range { start :: Int, len :: Int }
11+
12+
parseAlmanac :: String -> Almanac
13+
parseAlmanac s = case parse almanac "" s of
14+
Left err -> error (show err)
15+
Right v -> v
16+
where
17+
sp = char ' '
18+
num = read <$> many1 digit
19+
nums = num `sepBy` sp
20+
seeds = string "seeds: " *> nums <* count 2 newline
21+
mapHeader = many1 (letter <|> char '-' <|> sp) >> char ':'
22+
endOfLineOrFile = void endOfLine <|> eof
23+
rangeMapping = mkRangeMapping <$> (num <* sp) <*> (num <* sp) <*> num
24+
map = mapHeader *> newline *> (rangeMapping `endBy` endOfLineOrFile)
25+
maps = map `endBy` endOfLineOrFile
26+
almanac = Almanac <$> seeds <*> maps
27+
mkRangeMapping a b c = RangeMapping (Range b c) (Range a c)
28+
29+
p1 :: Almanac -> Int
30+
p1 Almanac { seeds, maps } = minimum $ map (`mTransform` maps) seeds
31+
32+
-- Guide a seed through the transformations under the given maps
33+
mTransform :: Int -> [Map] -> Int
34+
mTransform = foldl rmTransform
35+
36+
-- Transform a seed using the given range mappings
37+
rmTransform :: Int -> [RangeMapping] -> Int
38+
rmTransform s [] = s
39+
rmTransform s (rm:rms) = case rmApply rm s of
40+
Just s -> s
41+
Nothing -> rmTransform s rms
42+
43+
-- Apply the given range mapping to the seed if it lies in the source range.
44+
rmApply :: RangeMapping -> Int -> Maybe Int
45+
rmApply RangeMapping { from, to } s = case offsetInRange from s of
46+
Nothing -> Nothing
47+
Just o -> Just (start to + o)
48+
49+
-- If the given seed falls in the given range, then return its offset from the
50+
-- start of the range.
51+
offsetInRange :: Range -> Int -> Maybe Int
52+
offsetInRange Range { start, len } x =
53+
if x >= start && x <= (start + len) then Just (x - start) else Nothing
54+
55+
p2 :: Almanac -> Int
56+
p2 Almanac { seeds, maps } = minimum . map start . filter (\r -> len r /= 0) $
57+
foldl transformRanges (seedRanges seeds) maps
58+
59+
seedRanges :: [Int] -> [Range]
60+
seedRanges [] = []
61+
seedRanges (x:y:rest) = Range x y : seedRanges rest
62+
63+
-- Transform seed ranges under the given range map. This may result in more
64+
-- ranges than we started with.
65+
transformRanges :: [Range] -> Map -> [Range]
66+
transformRanges rs m = concatMap (`transformRange` m) rs
67+
68+
-- Transform a seed range under the given range mappings. Such a transformation
69+
-- may cause the range to split.
70+
transformRange :: Range -> [RangeMapping] -> [Range]
71+
transformRange r [] = [r]
72+
transformRange r (rm:rms) = concatMap transform (intersections r (from rm))
73+
where transform x = case mapRange rm x of
74+
Nothing -> transformRange x rms
75+
Just y -> [y]
76+
77+
-- Not necessarily symmetric.
78+
intersections :: Range -> Range -> [Range]
79+
intersections r@Range { start = s, len = n } r'@Range { start = s', len = n' }
80+
| s > e' = [r]
81+
| e < s' = [r]
82+
| s < s' = mk s (s' - 1) : if e <= e' then [mk s' e] else [mk s' e', mk (e' + 1) e]
83+
| s <= e' = if e <= e' then [mk s e] else [mk s e', mk (e' + 1) e]
84+
where e = s + n
85+
e' = s' + n'
86+
mk rs re = Range rs (re - rs)
87+
88+
-- This is guaranteed to be called with a range that does not cross over the
89+
-- boundaries of the 'from' range mapping (i.e. either it falls completely
90+
-- within, or is completely outside).
91+
mapRange :: RangeMapping -> Range -> Maybe Range
92+
mapRange RangeMapping { from, to } r@Range { start = s, len = n }
93+
| s >= start from && s <= (start from + len from) = Just $ Range (s - start from + start to) n
94+
| otherwise = Nothing
95+
96+
p2Brute :: Almanac -> Int
97+
p2Brute a = p1 $ a { seeds = expand (seeds a) }
98+
99+
expand :: [Int] -> [Int]
100+
expand [] = []
101+
expand (x:y:zs) = [x..(x+y)] ++ expand zs

0 commit comments

Comments
 (0)