https://adventofcode.com/2019/about One last week in November, and then it starts.
Are you competing this year?
What language(s) are you going to use?
Where are you going to talk about the puzzles? nano/g/ maybe?
If you've never participated, from 1Dec to Christmas a Christmas-themed programming puzzle is posted on midnight, and if you solve the puzzle you get a follow-up. The site records how long it takes you to answer both puzzles, and you can stay anonymous on the site. The easiest way to create an account is through linking a github.
The fun of Advent of Code
1. getting the first part done fast
2. adapting your part1 solution to part 2. Part2 tends to punish inefficient solutions and corner-cutting.
3. keeping track of how other people in your private group and on the top100 leaderboard are fairing with what you just did
4. posting your code and comparing it against other solutions. microbenchmarking, smoller-than-thouing
5. learning or getting comfortable with a new language. I've used two Advents this way. By the end of the contest you have the confidence required to embark on any programming task with the language used.
I am probably just going to boring and use Java this year. I used Haskell last year, but the challenges usually favor imperative languages making some day annoying.
>>9579 reddit's the least offensive option then, probably. create a throwaway reddit account, don't give them an email, use it as your AoC login.
>>9581 You've got a whole week to try get comfortable with Kotlin, you know.
I'd actually like to see Java solutions, but as a Christian I feel I should advise you against self-harm.
>>9593 cool. I will also be using a language with a borrow checker this time around.
Day 1: I got a late start and was tripped up a little by accidentally calculating the extra fuel needed for the fuel for the mass of the modules together instead of per module.
import Data.Function
totalFuelNeeded :: Integer -> Integer
totalFuelNeeded = fix totalFuel
where
totalFuel :: (Integer -> Integer) -> Integer -> Integer
totalFuel _ 0 = 0
totalFuel rest mass = let fuel = fuelNeeded mass in fuel + rest fuel
fuelNeeded :: Integer -> Integer
fuelNeeded mass = max 0 $ mass `div` 3 - 2
day1 :: IO ()
day1 = do
rawInput <- readFile "input"
let input = read <$> lines rawInput
print $ sum $ totalFuelNeeded <$> input
>>9866
The fix was not needed since I bound the totalFuel function to a label
totalFuel :: Integer -> Integer
totalFuel 0 = 0
totalFuel mass = let fuel = fuelNeeded mass in
fuel + totalFuel fuel
where
fuelNeeded :: Integer -> Integer
fuelNeeded mass = max 0 $ mass `div` 3 - 2
day1 :: IO ()
day1 = do
rawInput <- readFile "input"
let input = read <$> lines rawInput
print $ sum $ totalFuel <$> input
Today was pretty straight forward. You are just creating a simple virtual machine.
>>9887
You can see the first part of the challenge without logging in, but you will not be given an input, nor will you be able to check the solution. If you wanted I could post the parts, input, and answers **all separate of course** for you.
import Control.Arrow
import Data.List.Split
import Data.Vector as V hiding (filter)
runIntCode :: Vector Int -> Int
runIntCode code = V.head $ step 0 code
where
step :: Int -> Vector Int -> Vector Int
step ip code =
case toList $ V.drop ip code of
1 : x : y : z : _ -> step (ip + 4) $ code // [(z, (code ! x) + (code ! y))]
2 : x : y : z : _ -> step (ip + 4) $ code // [(z, (code ! x) * (code ! y))]
99 : _ -> code
Today was not very suited towards functional programming.
import Data.HashMap as HM
import Data.List
import Data.List.Split
data Direction
= U Integer
| L Integer
| R Integer
| D Integer
deriving (Show)
parseDirection ('U' : x) = U $ read x
parseDirection ('L' : x) = L $ read x
parseDirection ('R' : x) = R $ read x
parseDirection ('D' : x) = D $ read x
normalize :: [Direction] -> [Direction]
normalize [] = []
normalize (U 0 : xs) = normalize xs
normalize (L 0 : xs) = normalize xs
normalize (R 0 : xs) = normalize xs
normalize (D 0 : xs) = normalize xs
normalize (U d : xs) = U 1 : (normalize $ U (d - 1) : xs)
normalize (L d : xs) = L 1 : (normalize $ L (d - 1) : xs)
normalize (R d : xs) = R 1 : (normalize $ R (d - 1) : xs)
normalize (D d : xs) = D 1 : (normalize $ D (d - 1) : xs)
trackDistance :: [Direction] -> Map (Int, Int) Int
trackDistance ds = aux 0 (0, 0) ds
where
aux :: Int -> (Int, Int) -> [Direction] -> Map (Int, Int) Int
aux _ _ [] = empty
aux n (x, y) (d : ds) =
let map = aux (n + 1) (offset d (x, y)) ds
in HM.insert (x, y) n map
manhattan :: (Int, Int) -> Int
manhattan (x, y) = abs x + abs y
day3 :: IO ()
day3 = do
rawInput <- readFile "input"
let input = fmap parseDirection <$> splitOn "," <$> lines rawInput
let d1 = trackDistance $ normalize $ input !! 0
let d2 = trackDistance $ normalize $ input !! 1
print $ head $ drop 1 $ sort $ manhattan <$> (keys $ intersection d1 d2)
print $ head $ drop 1 $ sort $ elems $ intersectionWith (+) d1 d2
Got part 1 quickly, but had to spend a while debugging part 2 to find a simple mistake where I misinterpreted the specification. I hope you did not delete, day 2.
runIntCode :: Vector Int -> Int -> [Int]
runIntCode code ii = step 0 code
where
step :: Int -> Vector Int -> [Int]
step ip code =
let start = V.drop ip code
param1 = start ! 0 `div` 100 `mod` 10 == 1
param2 = start ! 0 `div` 1000 `mod` 10 == 1
param3 = start ! 0 `div` 10000 `mod` 10 == 1
in case V.head start `mod` 100 : (toList $ V.tail start) of
1 : x : y : z : _ -> step (ip + 4) $ code // [(z, (if param1 then x else code ! x) + (if param2 then y else code ! y))]
2 : x : y : z : _ -> step (ip + 4) $ code // [(z, (if param1 then x else code ! x) * (if param2 then y else code ! y))]
3 : x : _ -> step (ip + 2) $ code // [(x, ii)]
4 : x : _ -> (if param1 then x else code ! x) : (step (ip + 2) code)
5 : x : y : _ -> let new = if param1 then x else code ! x in step (if new /= 0 then if param2 then y else code ! y else ip + 3) code
6 : x : y : _ -> let new = if param1 then x else code ! x in step (if new == 0 then if param2 then y else code ! y else ip + 3) code
7 : x : y : z : _ -> step (ip + 4) $ code // if (if param1 then x else code ! x) < (if param2 then y else code ! y) then [(z, 1)] else [(z, 0)]
8 : x : y : z : _ -> step (ip + 4) $ code // if (if param1 then x else code ! x) == (if param2 then y else code ! y) then [(z, 1)] else [(z, 0)]
99 : _ -> []
day5 :: IO ()
day5 = do
rawInput <- readFile "input"
let input = fromList $ read <$> splitOn "," rawInput
let ii = 5
print $ runIntCode input ii
> that based anon who is ready to sacrifice his anonymity in favour of shitposting ready solutions
nice work, mate, but could you please elaborate what language is this >>9958 ?
Thanks to Haskell's lazyness I finally got my first top 50 in part 2. I was surprised when the recursive let actually worked.
import Data.List
import Data.List.Split
import Data.Vector as V hiding (filter)
runIntCode :: Vector Int -> [Int] -> [Int]
runIntCode code inputs = step 0 code inputs
where
step :: Int -> Vector Int -> [Int] -> [Int]
step ip code inputs =
let start = V.drop ip code
param1 = start ! 0 `div` 100 `mod` 10 == 1
param2 = start ! 0 `div` 1000 `mod` 10 == 1
param3 = start ! 0 `div` 10000 `mod` 10 == 1
in case V.head start `mod` 100 : (toList $ V.tail start) of
1 : x : y : z : _ -> step (ip + 4) (code // [(z, (if param1 then x else code ! x) + (if param2 then y else code ! y))]) inputs
2 : x : y : z : _ -> step (ip + 4) (code // [(z, (if param1 then x else code ! x) * (if param2 then y else code ! y))]) inputs
3 : x : _ -> step (ip + 2) (code // [(x, Data.List.head inputs)]) $ Data.List.tail inputs
4 : x : _ -> (if param1 then x else code ! x) : (step (ip + 2) code inputs)
5 : x : y : _ -> let new = if param1 then x else code ! x in step (if new /= 0 then if param2 then y else code ! y else ip + 3) code inputs
6 : x : y : _ -> let new = if param1 then x else code ! x in step (if new == 0 then if param2 then y else code ! y else ip + 3) code inputs
7 : x : y : z : _ -> step (ip + 4) (code // if (if param1 then x else code ! x) < (if param2 then y else code ! y) then [(z, 1)] else [(z, 0)]) inputs
8 : x : y : z : _ -> step (ip + 4) (code // if (if param1 then x else code ! x) == (if param2 then y else code ! y) then [(z, 1)] else [(z, 0)]) inputs
99 : _ -> []
runAmplifiers :: Vector Int -> [Int] -> Int
runAmplifiers input [v, w, x, y, z] =
let a = runIntCode input (v : 0 : e)
b = runIntCode input (w : a)
c = runIntCode input (x : b)
d = runIntCode input (y : c)
e = runIntCode input (z : d)
in Data.List.last e
day7 :: IO ()
day7 = do
rawInput <- readFile "input"
let input = fromList $ read <$> splitOn "," rawInput
print $ Data.List.last $ sort $ runAmplifiers input <$> filter (\[v, w, x, y, z] -> not (v == w || v == x || v == y || v == z || w == x || w == y || w == z || x == y || x == z || y == z)) ((\v w x y z -> [v, w, x, y, z]) <$> [0 .. 4] <*> [0 .. 4] <*> [0 .. 4] <*> [0 .. 4] <*> [0 .. 4])
print $ Data.List.last $ sort $ runAmplifiers input <$> filter (\[v, w, x, y, z] -> not (v == w || v == x || v == y || v == z || w == x || w == y || w == z || x == y || x == z || y == z)) ((\v w x y z -> [v, w, x, y, z]) <$> [5 .. 9] <*> [5 .. 9] <*> [5 .. 9] <*> [5 .. 9] <*> [5 .. 9])
Finally a chance to whip out fmap fmap fmap fmap fmap fmap fmap fmap. The pixel combination made a good monoid, so I wanted to implement it using one.
Day 8: http://aoc3ernictpwupe5o7lahkovoag6f23ka54q32hq6hspuj3bb5asgvad.onion/2019/day/8/
Day 8 Input: http://aoc3ernictpwupe5o7lahkovoag6f23ka54q32hq6hspuj3bb5asgvad.onion/2019/day/8/input
Day 8 Solutions: http://aoc3ernictpwupe5o7lahkovoag6f23ka54q32hq6hspuj3bb5asgvad.onion/2019/day/8/solutions
Finally I can put to rest the mess of code I have been using for the intcode computer. Today was the last challenge using it. A really useful hint for part one is that if you made a mistake implementing the new changes it will output the instruction and some of its operands to help you debug the issue. It was very helpful in finding the instruction I had messed up on.
Day 9: http://aoc3ernictpwupe5o7lahkovoag6f23ka54q32hq6hspuj3bb5asgvad.onion/2019/day/9/
Day 9 Input: http://aoc3ernictpwupe5o7lahkovoag6f23ka54q32hq6hspuj3bb5asgvad.onion/2019/day/9/input
Day 9 Solutions: http://aoc3ernictpwupe5o7lahkovoag6f23ka54q32hq6hspuj3bb5asgvad.onion/2019/day/9/solutions
import Data.List
import Data.List.Split
import Data.Vector as V hiding (filter)
runIntCode :: Vector Int -> [Int] -> [Int]
runIntCode code inputs = step 0 code inputs 0
where
step :: Int -> Vector Int -> [Int] -> Int -> [Int]
step ip code inputs base =
let start = V.drop ip code
param1 = start ! 0 `div` 100 `mod` 10
param2 = start ! 0 `div` 1000 `mod` 10
param3 = start ! 0 `div` 10000 `mod` 10
in case V.head start `mod` 100 : (toList $ V.tail start) of
1 : x : y : z : _ -> step (ip + 4) (code // [(if param3 == 2 then base + z else z, (if param1 == 2 then code ! (base + x) else (if param1 == 1 then x else code ! x)) + (if param2 == 2 then code ! (base + y) else (if param2 == 1 then y else code ! y)))]) inputs base
2 : x : y : z : _ -> step (ip + 4) (code // [(if param3 == 2 then base + z else z, (if param1 == 2 then code ! (base + x) else (if param1 == 1 then x else code ! x)) * (if param2 == 2 then code ! (base + y) else (if param2 == 1 then y else code ! y)))]) inputs base
3 : x : _ -> step (ip + 2) (code // [(if param1 == 2 then base + x else x, Data.List.head inputs)]) (Data.List.tail inputs) base
4 : x : _ -> (if param1 == 2 then code ! (base + x) else (if param1 == 1 then x else code ! x)) : (step (ip + 2) code inputs base)
5 : x : y : _ -> let new = if param1 == 2 then code ! (base + x) else (if param1 == 1 then x else code ! x) in step (if new /= 0 then if param2 == 2 then code ! (base + y) else (if param2 == 1 then y else code ! y) else ip + 3) code inputs base
6 : x : y : _ -> let new = if param1 == 2 then code ! (base + x) else (if param1 == 1 then x else code ! x) in step (if new == 0 then if param2 == 2 then code ! (base + y) else (if param2 == 1 then y else code ! y) else ip + 3) code inputs base
7 : x : y : z : _ -> step (ip + 4) (code // if (if param1 == 2 then code ! (base + x) else (if param1 == 1 then x else code ! x)) < (if param2 == 2 then code ! (base + y) else (if param2 == 1 then y else code ! y)) then [(if param3 == 2 then base + z else z, 1)] else [(if param3 == 2 then base + z else z, 0)]) inputs base
8 : x : y : z : _ -> step (ip + 4) (code // if (if param1 == 2 then code ! (base + x) else (if param1 == 1 then x else code ! x)) == (if param2 == 2 then code ! (base + y) else (if param2 == 1 then y else code ! y)) then [(if param3 == 2 then base + z else z, 1)] else [(if param3 == 2 then base + z else z, 0)]) inputs base
9 : x : _ -> step (ip + 2) code inputs (base + (if param1 == 2 then code ! (base + x) else (if param1 == 1 then x else code ! x)))
99 : _ -> []
The atan2 function came in handy today, but I had to override the edge case for when the point exists on the "positive x axis."
Day 10: http://aoc3ernictpwupe5o7lahkovoag6f23ka54q32hq6hspuj3bb5asgvad.onion/2019/day/10/
Day 10 Input: http://aoc3ernictpwupe5o7lahkovoag6f23ka54q32hq6hspuj3bb5asgvad.onion/2019/day/10/input
Day 10 Solutions: http://aoc3ernictpwupe5o7lahkovoag6f23ka54q32hq6hspuj3bb5asgvad.onion/2019/day/10/solutions
import Control.Arrow
import Data.List
sides :: (Int, Int) -> [(Int, Int)] -> Int
sides _ [] = 0
sides (x, _) ps = if foldr (\(px, _) acc -> px < x && acc) True ps then 1 else 2
canSee :: [(Int, Int)] -> (Int, Int) -> (Int, Int) -> Bool
canSee ps (x, y) (px, py) =
(x, y) == (px, py)
|| if px == x
then
let colinear = filter (\(ax, _) -> ax == x) ps
los = filter (\(_, ay) -> if py < y then ay < y else ay > y) colinear
closest = head $ sortOn (\(ax, ay) -> (ay - y) ^ 2) los
in (px, py) == closest
else
let ratio = fromIntegral (py - y) / fromIntegral (px - x)
colinear = fst <$> (filter ((== ratio) . snd) $ (id &&& (\(ax, ay) -> fromIntegral (ay - y) / fromIntegral (ax - x))) <$> ps)
los = filter (\(ax, _) -> if px < x then ax < x else ax > x) colinear
closest = head $ sortOn (\(ax, ay) -> (ax - x) ^ 2 + (ay - y) ^ 2) los
in (px, py) == closest
Did not have enough time to finish yesterday's challenge, but here it is. It was another Intcode problem but now the computer needed to support doing computations between inputs and outputs. I kind of gave up on this challenge and just wrote enough code until it worked. I really hope I do not need to return to this mess.
Day 11: http://aoc3ernictpwupe5o7lahkovoag6f23ka54q32hq6hspuj3bb5asgvad.onion/2019/day/11/
Day 11 Input: http://aoc3ernictpwupe5o7lahkovoag6f23ka54q32hq6hspuj3bb5asgvad.onion/2019/day/11/input
Day 11 Solutions: http://aoc3ernictpwupe5o7lahkovoag6f23ka54q32hq6hspuj3bb5asgvad.onion/2019/day/11/solutions
import Data.HashMap as HM hiding ((!))
import Data.List
import Data.List.Split
import Data.Vector as V hiding (elem, filter)
runIntCode :: Vector Int -> Bool -> [Int] -> [Int]
runIntCode code part2 inputs = step 0 code inputs 0 [] [] ([], U, (0, 0))
where
step :: Int -> Vector Int -> [Int] -> Int -> [Int] -> [Int] -> ([(Int, Int)], Direction, (Int, Int)) -> [Int]
step ip code inputs base outBuf output state =
let start = V.drop ip code
param1 = start ! 0 `div` 100 `mod` 10
param2 = start ! 0 `div` 1000 `mod` 10
param3 = start ! 0 `div` 10000 `mod` 10
in case V.head start `mod` 100 : (V.toList $ V.tail start) of
1 : x : y : z : _ -> step (ip + 4) (code // [(if param3 == 2 then base + z else z, (if param1 == 2 then code ! (base + x) else (if param1 == 1 then x else code ! x)) + (if param2 == 2 then code ! (base + y) else (if param2 == 1 then y else code ! y)))]) inputs base outBuf output state
2 : x : y : z : _ -> step (ip + 4) (code // [(if param3 == 2 then base + z else z, (if param1 == 2 then code ! (base + x) else (if param1 == 1 then x else code ! x)) * (if param2 == 2 then code ! (base + y) else (if param2 == 1 then y else code ! y)))]) inputs base outBuf output state
3 : x : _ -> let (inp, s) = (if part2 then runBot3 else runBot2) state outBuf in step (ip + 2) (code // [(if param1 == 2 then base + x else x, inp)]) inputs base (Data.List.drop 2 outBuf) output s
4 : x : _ -> let new = [if param1 == 2 then code ! (base + x) else (if param1 == 1 then x else code ! x)] in step (ip + 2) code inputs base (outBuf <> new) (output <> new) state
5 : x : y : _ -> let new = if param1 == 2 then code ! (base + x) else (if param1 == 1 then x else code ! x) in step (if new /= 0 then if param2 == 2 then code ! (base + y) else (if param2 == 1 then y else code ! y) else ip + 3) code inputs base outBuf output state
6 : x : y : _ -> let new = if param1 == 2 then code ! (base + x) else (if param1 == 1 then x else code ! x) in step (if new == 0 then if param2 == 2 then code ! (base + y) else (if param2 == 1 then y else code ! y) else ip + 3) code inputs base outBuf output state
7 : x : y : z : _ -> step (ip + 4) (code // if (if param1 == 2 then code ! (base + x) else (if param1 == 1 then x else code ! x)) < (if param2 == 2 then code ! (base + y) else (if param2 == 1 then y else code ! y)) then [(if param3 == 2 then base + z else z, 1)] else [(if param3 == 2 then base + z else z, 0)]) inputs base outBuf output state
8 : x : y : z : _ -> step (ip + 4) (code // if (if param1 == 2 then code ! (base + x) else (if param1 == 1 then x else code ! x)) == (if param2 == 2 then code ! (base + y) else (if param2 == 1 then y else code ! y)) then [(if param3 == 2 then base + z else z, 1)] else [(if param3 == 2 then base + z else z, 0)]) inputs base outBuf output state
9 : x : _ -> step (ip + 2) code inputs (base + (if param1 == 2 then code ! (base + x) else (if param1 == 1 then x else code ! x))) outBuf output state
99 : _ -> output
data Direction
= U
| L
| R
| D
deriving (Show)
offset :: Direction -> (Int, Int) -> (Int, Int)
offset U (x, y) = (x, y + 1)
offset L (x, y) = (x - 1, y)
offset R (x, y) = (x + 1, y)
offset D (x, y) = (x, y - 1)
left :: Direction -> Direction
left U = L
left L = D
left R = U
left D = R
right :: Direction -> Direction
right U = R
right L = U
right R = D
right D = L
runBot3 :: ([(Int, Int)], Direction, (Int, Int)) -> [Int] -> (Int, ([(Int, Int)], Direction, (Int, Int)))
runBot3 _ [] = (1, ([(0, 0)], U, (0, 0)))
runBot3 (white, dir, pos) (a : b : os) =
let newWhite = if a == 1 then Data.List.union white [pos] else Data.List.delete pos white
newDir = if b == 1 then right dir else left dir
newPos = offset newDir pos
color = if newPos `elem` newWhite then 1 else 0
in (color, (newWhite, newDir, newPos))
runBot2 :: ([(Int, Int)], Direction, (Int, Int)) -> [Int] -> (Int, ([(Int, Int)], Direction, (Int, Int)))
runBot2 _ [] = (0, ([], U, (0, 0)))
runBot2 (white, dir, pos) (a : b : os) =
let newWhite = if a == 1 then Data.List.union white [pos] else Data.List.delete pos white
newDir = if b == 1 then right dir else left dir
newPos = offset newDir pos
color = if newPos `elem` newWhite then 1 else 0
in (color, (newWhite, newDir, newPos))
runBot :: [(Int, Int)] -> Direction -> (Int, Int) -> [Int] -> [(Int, (Int, Int))]
runBot white dir pos [] = [(if pos `elem` white then 1 else 0, pos)]
runBot white dir pos (a : b : os) =
let newWhite = if a == 1 then Data.List.union white [pos] else Data.List.delete pos white
newDir = if b == 1 then right dir else left dir
newPos = offset newDir pos
color = if pos `elem` newWhite then 1 else 0
in (color, pos) : runBot newWhite newDir newPos os
printCoords :: [(Int, (Int, Int))] -> IO ()
printCoords coords =
let map = Data.List.foldr (\(color, pos) acc -> HM.insert pos color acc) HM.empty coords
in Prelude.mapM_ print $ (\x -> (\y -> case HM.lookup (y, - x) map of Just 1 -> '#'; _ -> ' ') <$> [-100 .. 100]) <$> [-100 .. 100]
day11 :: IO ()
day11 = do
rawInput <- readFile "input"
let input = V.fromList $ (read <$> splitOn "," rawInput) <> (Data.List.take 1000 $ repeat 0)
let output = runIntCode input False []
let coords = runBot [(0, 0)] U (0, 0) output
let res = nubBy (\(_, x) (_, y) -> x == y) $ Data.List.reverse coords
print $ Data.List.length res
let output2 = runIntCode input True []
let coords2 = runBot [(0, 0)] U (0, 0) output2
let res2 = nubBy (\(_, x) (_, y) -> x == y) $ Data.List.reverse coords2
printCoords res2
Pretty standard problem if you have done an AoC of a previous year.
Day 11: http://aoc3ernictpwupe5o7lahkovoag6f23ka54q32hq6hspuj3bb5asgvad.onion/2019/day/12/
Day 11 Input: http://aoc3ernictpwupe5o7lahkovoag6f23ka54q32hq6hspuj3bb5asgvad.onion/2019/day/12/input
Day 11 Solutions: http://aoc3ernictpwupe5o7lahkovoag6f23ka54q32hq6hspuj3bb5asgvad.onion/2019/day/12/solutions
import Data.HashMap as HM hiding ((!))
import Data.List
import Data.List.Split
comp :: Int -> Int -> Int
comp x y = if x > y then -1 else if x < y then 1 else 0
getVelocities :: [Int] -> (Int) -> (Int)
getVelocities pos x = sum $ (\a -> comp x a) <$> pos
step :: [Int] -> [Int] -> ([Int], [Int])
step pos vel =
let newVel = applyGravity pos vel
in (zipWith (+) pos newVel, newVel)
runSteps :: [Int] -> Int -> ([Int], [Int])
runSteps pos n = runStepsAux pos (take (length pos) $ repeat 0) n
where
runStepsAux :: [Int] -> [Int] -> Int -> ([Int], [Int])
runStepsAux pos vel 0 = (pos, vel)
runStepsAux pos vel n =
let (newPos, newVel) = step pos vel
in runStepsAux newPos newVel $ n - 1
runStepsMatch :: [Int] -> Int
runStepsMatch pos = runStepsAux pos (take (length pos) $ repeat 0) HM.empty 0
where
runStepsAux :: [Int] -> [Int] -> Map ([Int], [Int]) Int -> Int -> Int
runStepsAux pos vel map n =
if member (pos, vel) map
then n
else
let (newPos, newVel) = step pos vel
in runStepsAux newPos newVel (HM.insert (pos, vel) n map) $ n + 1
energy :: (Int, Int, Int) -> (Int, Int, Int) -> Int
energy (a, b, c) (x, y, z) = (abs a + abs b + abs c) * (abs x + abs y + abs z)
day12 :: IO ()
day12 = do
rawInput <- readFile "input"
let input = (\[x, y, z] -> (read x, read y, read z)) <$> ((fmap $ drop 2) <$> splitOn ", ") <$> (tail . init) <$> lines rawInput
let xs = (\(x, _, _) -> x) <$> input
let ys = (\(_, y, _) -> y) <$> input
let zs = (\(_, _, z) -> z) <$> input
let (finalPosX, finalVelX) = runSteps xs 1000
let (finalPosY, finalVelY) = runSteps ys 1000
let (finalPosZ, finalVelZ) = runSteps zs 1000
let finalPos = zip3 finalPosX finalPosY finalPosZ
let finalVel = zip3 finalVelX finalVelY finalVelZ
print $ sum $ (\(pos, vel) -> energy pos vel) <$> zip finalPos finalVel
let cx = runStepsMatch xs
let cy = runStepsMatch ys
let cz = runStepsMatch zs
print $ lcm cx $ lcm cy cz
>>10185
Today involved running breakout on your intcode computer.
Day 13: http://aoc3ernictpwupe5o7lahkovoag6f23ka54q32hq6hspuj3bb5asgvad.onion/2019/day/13/
Day 13 Input: http://aoc3ernictpwupe5o7lahkovoag6f23ka54q32hq6hspuj3bb5asgvad.onion/2019/day/13/input
Day 13 Solutions: http://aoc3ernictpwupe5o7lahkovoag6f23ka54q32hq6hspuj3bb5asgvad.onion/2019/day/13/solutions
import Data.HashMap as HM hiding ((!))
import Data.List
import Data.List.Split
import Data.Vector as V hiding (elem, filter)
runIntCode :: Vector Int -> (([Int], (Int, Int)) -> ([Int], (Int, Int))) -> (Int, Int) -> [Int] -> [Int]
runIntCode code pollInput st inputs = step 0 code inputs 0 [] [] st
where
step :: Int -> Vector Int -> [Int] -> Int -> [Int] -> [Int] -> (Int, Int) -> [Int]
step ip code inputs base outBuf output state =
let start = V.drop ip code
param1 = start ! 0 `div` 100 `mod` 10
param2 = start ! 0 `div` 1000 `mod` 10
param3 = start ! 0 `div` 10000 `mod` 10
in case V.head start `mod` 100 : (V.toList $ V.tail start) of
1 : x : y : z : _ -> step (ip + 4) (code // [(if param3 == 2 then base + z else z, (if param1 == 2 then code ! (base + x) else (if param1 == 1 then x else code ! x)) + (if param2 == 2 then code ! (base + y) else (if param2 == 1 then y else code ! y)))]) inputs base outBuf output state
2 : x : y : z : _ -> step (ip + 4) (code // [(if param3 == 2 then base + z else z, (if param1 == 2 then code ! (base + x) else (if param1 == 1 then x else code ! x)) * (if param2 == 2 then code ! (base + y) else (if param2 == 1 then y else code ! y)))]) inputs base outBuf output state
3 : x : _ -> let (inp, s) = pollInput (outBuf, state) in step (ip + 2) (code // [(if param1 == 2 then base + x else x, (Data.List.head (inputs <> inp)))]) (Data.List.tail (inputs <> inp)) base [] output s
4 : x : _ -> let new = [if param1 == 2 then code ! (base + x) else (if param1 == 1 then x else code ! x)] in step (ip + 2) code inputs base (outBuf <> new) (output <> new) state
5 : x : y : _ -> let new = if param1 == 2 then code ! (base + x) else (if param1 == 1 then x else code ! x) in step (if new /= 0 then if param2 == 2 then code ! (base + y) else (if param2 == 1 then y else code ! y) else ip + 3) code inputs base outBuf output state
6 : x : y : _ -> let new = if param1 == 2 then code ! (base + x) else (if param1 == 1 then x else code ! x) in step (if new == 0 then if param2 == 2 then code ! (base + y) else (if param2 == 1 then y else code ! y) else ip + 3) code inputs base outBuf output state
7 : x : y : z : _ -> step (ip + 4) (code // if (if param1 == 2 then code ! (base + x) else (if param1 == 1 then x else code ! x)) < (if param2 == 2 then code ! (base + y) else (if param2 == 1 then y
else code ! y)) then [(if param3 == 2 then base + z else z, 1)] else [(if param3 == 2 then base + z else z, 0)]) inputs base outBuf output state
8 : x : y : z : _ -> step (ip + 4) (code // if (if param1 == 2 then code ! (base + x) else (if param1 == 1 then x else code ! x)) == (if param2 == 2 then code ! (base + y) else (if param2 == 1 then y else code ! y)) then [(if param3 == 2 then base + z else z, 1)] else [(if param3 == 2 then base + z else z, 0)]) inputs base outBuf output state
9 : x : _ -> step (ip + 2) code inputs (base + (if param1 == 2 then code ! (base + x) else (if param1 == 1 then x else code ! x))) outBuf output state
99 : _ -> output
poll :: ([Int], (Int, Int)) -> ([Int], (Int, Int))
poll (output, (x, y)) =
let out = Data.List.reverse $ chunksOf 3 output
pads = Data.List.filter (\[x, y, z] -> z == 3) out
pad = if Data.List.null pads then (x, y) else (\[px, py, 3] -> (px, py)) $ Data.List.head pads
balls = Data.List.filter (\[x, y, z] -> z == 4) out
in if Data.List.null balls
then ([], pad)
else
let [bx, by, 4] = Data.List.head balls
in if bx > fst pad
then ([1], pad)
else
if bx < fst pad
then ([-1], pad)
else ([0], pad)
tile :: Int -> Char
tile t =
case t of
0 -> ' '
1 -> '#'
2 -> '@'
3 -> '_'
4 -> 'o'
_ -> '?'
printCoords :: Map (Int, Int) Int -> [[Int]] -> IO (Map (Int, Int) Int)
printCoords map coords =
let newMap = Data.List.foldr (\[x, y, z] acc -> HM.insert (x, y) z acc) map coords
in do
Prelude.mapM_ print $ (\x -> (\y -> case HM.lookup (y, x) newMap of Just z -> tile z; _ -> ' ') <$> [0 .. 45]) <$> [0 .. 25]
print $ HM.lookup (-1, 0) newMap
pure newMap
One last week in November, and then it starts.
Are you competing this year?
What language(s) are you going to use?
Where are you going to talk about the puzzles? nano/g/ maybe?
If you've never participated, from 1Dec to Christmas a Christmas-themed programming puzzle is posted on midnight, and if you solve the puzzle you get a follow-up. The site records how long it takes you to answer both puzzles, and you can stay anonymous on the site. The easiest way to create an account is through linking a github.
The fun of Advent of Code
1. getting the first part done fast
2. adapting your part1 solution to part 2. Part2 tends to punish inefficient solutions and corner-cutting.
3. keeping track of how other people in your private group and on the top100 leaderboard are fairing with what you just did
4. posting your code and comparing it against other solutions. microbenchmarking, smoller-than-thouing
5. learning or getting comfortable with a new language. I've used two Advents this way. By the end of the contest you have the confidence required to embark on any programming task with the language used.