@@ -29,3 +29,9 @@ executable day01
|
|||||||
hs-source-dirs: src
|
hs-source-dirs: src
|
||||||
main-is: Day01.hs
|
main-is: Day01.hs
|
||||||
build-depends: libaoc
|
build-depends: libaoc
|
||||||
|
|
||||||
|
executable day02
|
||||||
|
import: common
|
||||||
|
hs-source-dirs: src
|
||||||
|
main-is: Day02.hs
|
||||||
|
build-depends: libaoc
|
||||||
|
|||||||
71
src/Day02.hs
Normal file
71
src/Day02.hs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
import qualified AoC as A (extract)
|
||||||
|
import Data.List (nub)
|
||||||
|
import Text.Parsec (char, digit, many1, parse, sepEndBy1)
|
||||||
|
import Text.Parsec.String (Parser)
|
||||||
|
|
||||||
|
type Id = Int
|
||||||
|
|
||||||
|
type Range = (Id, Id)
|
||||||
|
|
||||||
|
parseRanges :: Parser [Range]
|
||||||
|
parseRanges = sepEndBy1 parseRange (char ',')
|
||||||
|
where
|
||||||
|
parseRange :: Parser Range
|
||||||
|
parseRange = liftA2 (,) parseId (char '-' *> parseId)
|
||||||
|
|
||||||
|
parseId :: Parser Id
|
||||||
|
parseId = read <$> many1 digit
|
||||||
|
|
||||||
|
-- Imagine the number is of form xx where x is the repeated k digit number
|
||||||
|
-- Note that xx is of length 2 * k, since x is of length k, and k is log_10(x)
|
||||||
|
-- The number will always be of the form x + (10 ^ k) * x or x * (10 ^ k + 1)
|
||||||
|
-- Hence, in modular arithmetic, xx = x (mod (10 ^ k + 1))
|
||||||
|
part1 :: [Range] -> Int
|
||||||
|
part1 = sum . map invalids
|
||||||
|
where
|
||||||
|
invalids :: Range -> Int
|
||||||
|
invalids (l, r) =
|
||||||
|
let len = length . show $ r
|
||||||
|
reps = 2
|
||||||
|
k = len `div` reps
|
||||||
|
m = 10 ^ k + 1
|
||||||
|
in sum
|
||||||
|
[ xx
|
||||||
|
| x <-
|
||||||
|
[ ceiling (fromIntegral l / fromIntegral m)
|
||||||
|
.. floor (fromIntegral r / fromIntegral m)
|
||||||
|
],
|
||||||
|
let xx = x * m,
|
||||||
|
length (show xx) `mod` reps == 0
|
||||||
|
]
|
||||||
|
|
||||||
|
-- Second part is similar to first part except that it is now a geometric
|
||||||
|
-- progression of form x + (10 ^ k) * x .. (10 ^ (n - 1) * k) * x, where n is the
|
||||||
|
-- number of repetitions (2 in part1). So using similar logic and sum for
|
||||||
|
-- geometric progression we get:
|
||||||
|
part2 :: [Range] -> Int
|
||||||
|
part2 = sum . map invalids
|
||||||
|
where
|
||||||
|
invalids :: Range -> Int
|
||||||
|
invalids (l, r) =
|
||||||
|
let len = length . show $ r
|
||||||
|
in sum . nub $
|
||||||
|
[ xx
|
||||||
|
| reps <- [2 .. len],
|
||||||
|
let k = len `div` reps,
|
||||||
|
let m = (10 ^ (reps * k) - 1) `div` (10 ^ k - 1),
|
||||||
|
x <-
|
||||||
|
[ ceiling (fromIntegral l / fromIntegral m)
|
||||||
|
.. floor (fromIntegral r / fromIntegral m)
|
||||||
|
],
|
||||||
|
let xx = x * m,
|
||||||
|
length (show xx) `mod` reps == 0
|
||||||
|
]
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main =
|
||||||
|
do
|
||||||
|
raw <- readFile "./inputs/day2.in"
|
||||||
|
let ranges = A.extract $ parse parseRanges "" raw
|
||||||
|
putStr "Part 1: " >> print (part1 ranges)
|
||||||
|
putStr "Part 2: " >> print (part2 ranges)
|
||||||
Reference in New Issue
Block a user