├── 1 ├── Makefile ├── README.md ├── input.txt ├── problem-a.md ├── problem-b.md └── soln.hs ├── 2 ├── Makefile ├── README.md ├── go-soln.go ├── input.txt ├── problem-a.md ├── problem-b.md └── soln.hs ├── 3 ├── Makefile ├── README.md ├── input.txt ├── problem-a.md ├── problem-b.md └── soln.hs ├── 4 ├── Makefile ├── README.md ├── input.txt ├── problem-a.md ├── problem-b.md └── soln.hs ├── 5 ├── Makefile ├── README.md ├── input.txt ├── problem-a.md ├── problem-b.md └── soln.hs ├── 6 ├── Makefile ├── README.md ├── input.txt ├── problem-a.md ├── problem-b.md └── soln.hs ├── 7 ├── Makefile ├── README.md ├── input.txt ├── problem-a.md ├── problem-b.md ├── soln.hs └── topo.txt ├── 8 ├── Makefile ├── README.md ├── input.txt ├── problem-a.md ├── problem-b.md └── soln.hs ├── 9 ├── Makefile ├── README.md ├── input.txt ├── problem-a.md ├── problem-b.md └── soln.hs ├── 10 ├── Makefile ├── README.md ├── input.txt ├── problem-a.md ├── problem-b.md └── soln.hs ├── 11 ├── Makefile ├── README.md ├── input.txt ├── problem-a.md ├── problem-b.md └── soln.hs ├── 12 ├── .gitignore ├── Makefile ├── README.md ├── findnumbers.sh ├── input.txt ├── numbers.txt ├── problem-a.md ├── problem-b.md ├── soln-aeson.hs ├── soln-json.hs └── soln.hs ├── 13 ├── Makefile ├── README.md ├── input.txt ├── problem-a.md ├── problem-b.md └── soln.hs ├── 14 ├── Makefile ├── README.md ├── input.txt ├── problem-a.md ├── problem-b.md └── soln.hs ├── 15 ├── .gitignore ├── Makefile ├── README.md ├── best-recipe.hs ├── input.txt ├── problem-a.md ├── problem-b.md └── soln.hs ├── 16 ├── Makefile ├── README.md ├── input.txt ├── problem-a.md ├── problem-b.md └── soln.hs ├── 17 ├── Makefile ├── README.md ├── input.txt ├── problem-a.md ├── problem-b.md └── soln.hs ├── 18 ├── .gitignore ├── Makefile ├── README.md ├── input.txt ├── life.hs ├── problem-a.md ├── problem-b.md └── soln.hs ├── 19 ├── Makefile ├── README.md ├── input.txt ├── problem-a.md ├── problem-b.md ├── soln.hs └── test.txt ├── 20 ├── Factor.hs ├── Makefile ├── README.md ├── input.txt ├── problem-a.md ├── problem-b.md └── soln.hs ├── 21 ├── Makefile ├── README.md ├── input.txt ├── problem-a.md ├── problem-b.md └── soln.hs ├── 22 ├── .gitignore ├── Makefile ├── README.md ├── input.txt ├── old-soln.hs ├── problem-a.md ├── problem-b.md └── soln.hs ├── 23 ├── Makefile ├── README.md ├── input.txt ├── problem-a.md ├── problem-b.md └── soln.hs ├── 24 ├── Makefile ├── README.md ├── input.txt ├── problem-a.md ├── problem-b.md └── soln.hs ├── 25 ├── Makefile ├── README.md ├── input.txt ├── problem.md └── soln.hs ├── .gitignore ├── COPYING ├── Makefile ├── README.md └── Soln ├── Makefile ├── Makefile.template ├── Soln.hs └── soln.hs.template /.gitignore: -------------------------------------------------------------------------------- 1 | *.hi 2 | *.o 3 | soln 4 | -------------------------------------------------------------------------------- /1/Makefile: -------------------------------------------------------------------------------- 1 | ../Soln/Makefile.template -------------------------------------------------------------------------------- /1/README.md: -------------------------------------------------------------------------------- 1 | This problem is pretty clearly intended as a warmup, and 2 | it's a nice one: gets us used to the setup and whatnot. 3 | 4 | This is pretty straightforward Haskell, but does nicely show 5 | the power of folds. The PartB solution uses the `Either` 6 | monad as an early termination mechanism. I really wanted to 7 | use the identifiers `traverse` and `floor`, so I blocked 8 | them out of the `Prelude` to avoid warnings. 9 | -------------------------------------------------------------------------------- /1/input.txt: -------------------------------------------------------------------------------- 1 | (((())))()((((((((())()(()))(()((((()(()(((()((()((()(()()()()()))(((()(()((((((((((())(()()((())()(((())))()(()(()((()(()))(()()()()((()((()(((()()(((((((()()())()((((()()(((((()(())()(())((())()()))()(((((((())(()())(()(((())(()))((())))(()((()())))()())((((())))(()(((((()(())(((()()((()((()((((((((((())(()())))))()))())()()((((()()()()()()((((((())())(((()())()((()()(((()()()))(((((()))(((()(()()()(()(()(((())()))(()(((()((())()(()())())))((()()()(()()(((()))(((()((((()(((((()()(()())((()())())(()((((((()(()()))((((()))))())((())()()((()(()))))((((((((()))(()()(((())())(())()((()()()()((()((()((()()(((())))(()((())()((((((((()((()(()()(((())())())))(())())))()((((()))))))())))()()))()())((()())()((()()()))(()()(((()(())((((())())((((((((()()()()())))()()()((((()()))))))()((((()(((()))(()()())))((()()(((()))()()())())(((())((()()(())()()()(((())))))()())((()))()))((())()()())()())()()(()))())))())()))(())((()(())))(()(())(()))))(()(())())(()(())(()(()))))((()())()))()((((()()))))())))()()())((())()((()()()))()(((()(()))))(())()()))(((()())))))))))(((())))()))())()))))()()(((())))))))()(()()(()))((()))))((())))((()((())))())))()()(()))())()(()((()())(()(()()())())(()()))()))))(()())()()))()()()()))(()(()(()))))))()(()))()))()()(()((())(()(())))()(((())(())())))))()(()(()))))()))(()()()(())()(()(())))()))))()()(((((())))))())()())())())()())()))))()))))))))())()()()()()()())))()))((())()))())))()((())()))))()))())))))))())()()()))()()(()((((()(((((((()(())((()())((()()))()))))(())))()()()(())((())()())))(())))(())))(((()()))()(())(((()(()))((())))())()))((((()))())()))))))))()(())())))(()))()(()()))())()()(())())))())()()(()())))()((()())(()(())(())))))))))))))(()))))()))))))()()())(()(((((()(()())))())()))(()))()))(()()))()())(()))())()(())((()()))))))())))())()(((())))(()(()))()()))()(()))))))((()())(()))))))()())))()()))))))))((((((((()()()(()))))))()())))())))()()((())()))((())(())))())())))()()()((()((()(())))())()(())))))))))()())))()()()()()()))()))((())())(()(()))))))(()()))()))(())))()))))))))))))(()))))))))()))))()))()())()))()()))))))()))))((()))))(()))())()(())))(()())((((()())))()))))(()))()(()()(())))))())))))()))))))())))())))))())))())())))())(()))))(())()(())))())()))((()()))))))())))((())))))))())))(())))))()()())))))())))))()))))))()))()()()(()(((()())())())(()))())))))((()(())(()))))))))(())))()()()())())(()))))()()()))()))())())())()(())))()(((()((((())))))))()))))))))))))))))))))((())()())(()))))()()))))))(()()(())())))())))((())))((())))))))))))))()))))()(()))))))())))))()))(()()())(()())))))))))()))))))(())))))()()))()())(((())))()))(()))))))))(())())))())))())())())()()))((())()(())()())()))()())(())(()))))()())))(()(((()))))))()(()())()()()))()))))))))()()()(())()())()(((((()))()())())(()))))()()()(())))())))()((()())))(()))())()(()())())(()))()()))((()()))((()()()()())))(())()))(()(())))((()()))))))))())))))))())()()))))))))))))))))(())()(())(())()())())()))()(()))))())())))))()())()(()))()()(())))(())())))))(()))))))))))))))())())(())(())))(((()))()))))())((())(()))())))))))())))))())))()))()))))))))))))())()))))()))))((()))(())))()(())))(())()))()))())))())))))))()(()())())))()()())))(())))))(()))))))))))))(()))()))()))())))(((()()()(())((()())))()())(((()))(())()))((()()()())))())(())(()))))()(((((())))(()))())())))))))((((()()()))())())()(()(()())))))))))()())())))(())))()())(((()(())())()()))())())))))))((()())((()()(()))(()(())))()))()))(()))(()))()()(()(((())((((()))()(()))((())()(()(()())()(()))()())))))(()))()))())()())))())))(())))((())(()())))))()))(())(()))()())()(()()((()(()))))))()(())(()())(())()))(((())()))(()()(()()()))))(()(())))()))))())))))())(()()()()()()(((())))(()()))()((())(((((()()())))(()))(()))()()))(((())())()(((()()()()))))(()))(())())))()())(()()())())))))))()))))((())))()())(()))(()(()))())))))())(())))))()()())())()))()()(())))(()))(())((((((())(()))(()))())()))(()()(())))()))(()()))()))()(())))(())))((()(()))(())()()())())))(((()()())(())()))))))()(((()(((((()()(((())(())))())()((()))))((()())()(())(((())))(((()((()(()(()))(()()))())(()))(())(())))()))))))((((()))()((((()(()))()))()()))))()(()(()))()(()((()(((()(()()(((()))))()(((()(()(()(((()(()())())()()(()(()())())(()((((())(()))()))(((((()()())(())()((()()())))()()(((()()))()((((((((()(())))())((()))))(())))(()))))((()((((()()(())(((((()))(((((((((((((()())))((((()(((()((())())()))((()))()(()()((()()()()(()()(()(()(((())()(()((((((()((()()((())()((((()((()()(()()())((()()()((()((())()(()(((()((())((((())(()))((()(()))(()())()((((((((()(((((((((((()))(()(((()(()()()((((())((())()())()))(())((())(()))(((()((()(())))(()))))((()()))))((((()(()(()())(()(())((((((((()((((()((()(((((()))())()(()))(()()((()(())(((((()(())()(((((()()))))))()(((())()(()()((((())()((())((()(((())(((()))((()()((((()(())))))((()((((()((()((()(((())((()))(((((((()(((()((((((((())()))((((())(((((()((((((((()(((()((()(((()()(((()((((((()()(()((((((((()()(()(()(())((((()())()))))(((()))((((())((((()())((()(())()((()((((((()((((((()(())))()())(((())())())()(())()(()())((()()((((())((((((())(()(((((()((((())()((((()(()(())(()())(((())()((())((((()))()((((((())(()(((()(((()((((((()(((()))(()()())())((()((()())()((((())(((()(()(((((((((())(())))()((()()()()(())((()))(((((((()(((((((((()(()))))(()((((((((()((((()((()()((((((()()(((((((()(()(())()(())((()()()((()(((((()())()(((((()())()()((()(()())(()()()(((()()(((((()((((((()()((()(()()()((((((((((((()((((((((()()(((()())))()(((()()(())())((((()((((()((((()()()(())(())((()(()(((((((((((((((()(())(())))))()()))((()(((()(())((()(((()(()()((((()()(((()(((()(((((()()((()(()(((()))((((((()((((((((()((()((())(((((()(((())(())())((()()))((((())()()((()(((()(((((()()(((()))(((()(()(((((((((((((()))((((((((()(((()))))())((((((((((((())((())((()())(((())((())(()((((((((((()(((())((()()(()((())(((((((((((()))((((((((((((()(()())((()((()((()(()(((()((((((((()()(()((()(()(((()))((()))(((((((((((((()(())((((((())(((()(())(()(()(()((()()))((((()((((()((((())))())((((()((((()))((((((()((((((()((()(((())))((())(()))(()((()((((()((()(((()()))((((()()()(((((((())(((())(()))())((((()())(((()(((((((((((()(()(()((()(((((((((((((((()()((((()((((((((()(((()()((()((((()))(((()(())((((((()((((())()((((()((()))(())()(()(((()((())())((((((()(()(())())(((())(()(()())(((((()((()((())()())(())))(((()(())))))))(((()(((()))()((()(((()()((()())()()))())))(((()))(()(((()(((((((((()(()(((((()()(((()())()()))))()(((()))(((()(()(()(()(()))()(())()))(()(((())))(()))))))))))(())((()((())((()(())()(())((()()((((()()((()()))((())(((()((()(())(())))()(()(((((()((()))())()(((((()()(((()(()((((((())(()))(())()))((()(()()))(())())()))(((())))(()((()(((())(())())))((()()((((((((((((((()((()(()()(()(((()))())()()((()()()(())(()))(()())(((())((())()(())()()(()()(())))((()(((()))))(((()()(()()))())((()((())()))((((()()()())((())))(((()(())(((((()(((((()((()(()((((()()(((()()()(((()())(((()()((((())(()))(((()))(())())((()))(((()((()))(((()()((())((()(((((()((((()()())((()))()((((()((()(()()()( -------------------------------------------------------------------------------- /1/problem-a.md: -------------------------------------------------------------------------------- 1 | Here's an easy puzzle to warm you up. 2 | 3 | Santa is trying to deliver presents in a large apartment building, but he can't find the right floor - the directions he got are a little confusing. He starts on the ground floor (floor 0) and then follows the instructions one character at a time. 4 | 5 | An opening parenthesis, (, means he should go up one floor, and a closing parenthesis, ), means he should go down one floor. 6 | 7 | The apartment building is very tall, and the basement is very deep; he will never find the top or bottom floors. 8 | 9 | For example: 10 | 11 | (()) and ()() both result in floor 0. 12 | ((( and (()(()( both result in floor 3. 13 | ))((((( also results in floor 3. 14 | ()) and ))( both result in floor -1 (the first basement level). 15 | ))) and )())()) both result in floor -3. 16 | To what floor do the instructions take Santa? -------------------------------------------------------------------------------- /1/problem-b.md: -------------------------------------------------------------------------------- 1 | Now, given the same instructions, find the position of the 2 | first character that causes him to enter the basement (floor 3 | -1). The first character in the instructions has position 1, 4 | the second character has position 2, and so on. 5 | 6 | For example: 7 | 8 | * `)` causes him to enter the basement at character position 1. 9 | * `()())` causes him to enter the basement at character 10 | position 5. 11 | 12 | What is the position of the character that causes Santa to 13 | first enter the basement? 14 | -------------------------------------------------------------------------------- /1/soln.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | 7 | import Prelude hiding (traverse, floor) 8 | import Soln 9 | 10 | -- | Strategy: Just traverse the floors. 11 | solna :: String -> IO () 12 | solna stuff = do 13 | print $ foldl' traverse 0 stuff 14 | where 15 | traverse :: Int -> Char -> Int 16 | traverse floor '(' = floor + 1 17 | traverse floor ')' = floor - 1 18 | traverse _ _ = error "bad direction" 19 | 20 | -- | 'Left' is number of steps needed to hit the basement. 21 | -- | 'Right' is number of steps so far, and current floor number. 22 | type State = Either Int (Int, Int) 23 | 24 | -- | Strategy: Traverse the floors keeping count. 25 | solnb :: String -> IO () 26 | solnb stuff = do 27 | case foldM traverse (1, 0) stuff of 28 | Left n -> print n 29 | Right _ -> error "basement never entered" 30 | where 31 | traverse :: (Int, Int) -> Char -> State 32 | traverse (count, floor) dirn = 33 | case dirn of 34 | '(' -> Right (count + 1, floor + 1) 35 | -- Going down from floor 0 enters the basement. 36 | ')' | floor == 0 -> Left count 37 | ')' -> Right (count + 1, floor - 1) 38 | _ -> error "bad direction" 39 | 40 | main :: IO () 41 | main = makeMain solna solnb 42 | -------------------------------------------------------------------------------- /10/Makefile: -------------------------------------------------------------------------------- 1 | ../Soln/Makefile.template -------------------------------------------------------------------------------- /10/README.md: -------------------------------------------------------------------------------- 1 | Pretty straightforward string-processing exercise. 2 | 3 | The Haskell makes use of laziness to good effect in `grow`, 4 | where `iterate` is used to generate an "infinite list" of 5 | expansions, but only the n-th one is returned. 6 | -------------------------------------------------------------------------------- /10/input.txt: -------------------------------------------------------------------------------- 1 | 3113322113 2 | -------------------------------------------------------------------------------- /10/problem-a.md: -------------------------------------------------------------------------------- 1 | Today, the Elves are playing a game called 2 | look-and-say. They take turns making sequences by reading 3 | aloud the previous sequence and using that reading as the 4 | next sequence. For example, 211 is read as "one two, two 5 | ones", which becomes 1221 (1 2, 2 1s). 6 | 7 | Look-and-say sequences are generated iteratively, using the 8 | previous value as input for the next step. For each step, 9 | take the previous value, and replace each run of digits 10 | (like 111) with the number of digits (3) followed by the 11 | digit itself (1). 12 | 13 | For example: 14 | 15 | * 1 becomes 11 (1 copy of digit 1). 16 | * 11 becomes 21 (2 copies of digit 1). 17 | * 21 becomes 1211 (one 2 followed by one 1). 18 | * 1211 becomes 111221 (one 1, one 2, and two 1s). 19 | * 111221 becomes 312211 (three 1s, two 2s, and one 1). 20 | 21 | Starting with the digits in your puzzle input, apply this 22 | process 40 times. What is the length of the result? 23 | -------------------------------------------------------------------------------- /10/problem-b.md: -------------------------------------------------------------------------------- 1 | Neat, right? You might also enjoy hearing John Conway 2 | talking about this sequence (that's Conway of Conway's Game 3 | of Life fame). 4 | 5 | Now, starting again with the digits in your puzzle input, 6 | apply this process 50 times. What is the length of the new 7 | result? 8 | -------------------------------------------------------------------------------- /10/soln.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | 7 | import Soln 8 | 9 | -- | Compute the "say-string" of an input string. 10 | say :: String -> String 11 | say s = 12 | concatMap sayGroup $ group s 13 | where 14 | sayGroup ds = show (length ds) ++ [head ds] 15 | 16 | -- | Compute the "say-string" resulting from `n` iterations 17 | -- of `say` on the input string. 18 | grow :: Int -> String -> String 19 | grow n s = iterate say s !! n 20 | 21 | -- | Strategy: Brute force 22 | soln :: Int -> String -> IO () 23 | soln n stuff = print $ length $ grow n $ head $ lines stuff 24 | 25 | solna :: String -> IO () 26 | solna stuff = do 27 | soln 40 stuff 28 | 29 | solnb :: String -> IO () 30 | solnb stuff = do 31 | soln 50 stuff 32 | 33 | main :: IO () 34 | main = makeMain solna solnb 35 | -------------------------------------------------------------------------------- /11/Makefile: -------------------------------------------------------------------------------- 1 | ../Soln/Makefile.template -------------------------------------------------------------------------------- /11/README.md: -------------------------------------------------------------------------------- 1 | This problem is eerily reminiscent of problem 5. Unlike 5, 2 | though, it is a bit ill-specified: it is not clear which 3 | "pairs of letters" count. Does "xaaax" have two, one or zero 4 | pairs? 5 | 6 | The Haskell here is pretty unremarkable. The hardest part of 7 | writing it was perfecting `incrChar`, which does an 8 | add-with-carry on a character of the string. 9 | -------------------------------------------------------------------------------- /11/input.txt: -------------------------------------------------------------------------------- 1 | hxbxwxba -------------------------------------------------------------------------------- /11/problem-a.md: -------------------------------------------------------------------------------- 1 | Santa's previous password expired, and he needs help 2 | choosing a new one. 3 | 4 | To help him remember his new password after the old one 5 | expires, Santa has devised a method of coming up with a 6 | password based on the previous one. Corporate policy 7 | dictates that passwords must be exactly eight lowercase 8 | letters (for security reasons), so he finds his new password 9 | by incrementing his old password string repeatedly until it 10 | is valid. 11 | 12 | Incrementing is just like counting with numbers: xx, xy, xz, 13 | ya, yb, and so on. Increase the rightmost letter one step; 14 | if it was z, it wraps around to a, and repeat with the next 15 | letter to the left until one doesn't wrap around. 16 | 17 | Unfortunately for Santa, a new Security-Elf recently 18 | started, and he has imposed some additional password 19 | requirements: 20 | 21 | * Passwords must include one increasing straight of at least 22 | three letters, like abc, bcd, cde, and so on, up to 23 | xyz. They cannot skip letters; abd doesn't count. 24 | 25 | * Passwords may not contain the letters i, o, or l, as these 26 | letters can be mistaken for other characters and are 27 | therefore confusing. 28 | 29 | * Passwords must contain at least two pairs of letters, like 30 | aa, bb, or zz. 31 | 32 | For example: 33 | 34 | * hijklmmn meets the first requirement (because it 35 | contains the straight hij) but fails the second 36 | requirement requirement (because it contains i and l). 37 | 38 | * abbceffg meets the third requirement (because it repeats 39 | bb and ff) but fails the first requirement. 40 | 41 | * abbcegjk fails the third requirement, because it only 42 | has one double letter (bb). 43 | 44 | * The next password after abcdefgh is abcdffaa. 45 | 46 | * The next password after ghijklmn is ghjaabcc, because 47 | you eventually skip all the passwords that start with 48 | ghi..., since i is not allowed. 49 | 50 | Given Santa's current password (your puzzle input), what 51 | should his next password be? 52 | -------------------------------------------------------------------------------- /11/problem-b.md: -------------------------------------------------------------------------------- 1 | Santa's password expired again. What's the next one? -------------------------------------------------------------------------------- /11/soln.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | 7 | import Soln 8 | 9 | -- | Is the given sequence of characters ascending? 10 | ascending :: String -> Bool 11 | ascending [c1, c2, c3] = 12 | ord c1 + 1 == ord c2 && 13 | ord c2 + 1 == ord c3 14 | ascending _ = error "bad tile length" 15 | 16 | -- | Does the given sequence of characters contain 17 | -- a confusing character? 18 | confused :: String -> Bool 19 | confused s = any (`elem` s) ['i', 'o', 'l'] 20 | 21 | -- | Count of the number of doubled-letter pairs. 22 | -- 23 | -- It is not obvious whether this meets the requirements of 24 | -- the problem, since it will count "xaaax" as one pair but 25 | -- "xaaaax" as two. The specification needs clarification. 26 | pairs :: String -> Int 27 | pairs s = 28 | sum $ map ((`div` 2) . length) (group s) 29 | 30 | -- | Lexicographic increment in the space of strings. 31 | incr :: String -> String 32 | incr s = 33 | fst $ foldr incrChar ("", 1) s 34 | where 35 | incrChar c (cs, carry) = 36 | let c' = ord(c) + carry - ord('a') in 37 | (chr ((c' `mod` 26) + ord 'a') : cs, 38 | 0 `max` (c' - 25)) 39 | 40 | -- | Strategy: Generate all successors of the first 41 | -- password, accepting the first valid one. 42 | nextPw :: String -> String 43 | nextPw s0 = 44 | case find valid $ tail $ iterate incr s0 of 45 | Just s -> s 46 | Nothing -> error "no valid next password" 47 | where 48 | valid s = 49 | any ascending (tiles 3 1 s) && 50 | not (confused s) && 51 | pairs s >= 2 52 | 53 | solna :: String -> IO () 54 | solna stuff = do 55 | putStrLn $ nextPw stuff 56 | 57 | solnb :: String -> IO () 58 | solnb stuff = do 59 | putStrLn $ nextPw $ nextPw stuff 60 | 61 | main :: IO () 62 | main = makeMain solna solnb 63 | -------------------------------------------------------------------------------- /12/.gitignore: -------------------------------------------------------------------------------- 1 | /soln-aeson 2 | /soln-json 3 | -------------------------------------------------------------------------------- /12/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright © 2015 Bart Massey 2 | # [This program is licensed under the "MIT License"] 3 | # Please see the file COPYING in the source 4 | # distribution of this software for license terms. 5 | 6 | HC = ghc 7 | HCFLAGS = -i../Soln -Wall -O2 8 | 9 | all: soln soln-aeson soln-json 10 | 11 | soln: soln.hs 12 | $(HC) $(HCFLAGS) --make soln.hs 13 | 14 | soln-aeson: soln-aeson.hs 15 | $(HC) $(HCFLAGS) --make soln-aeson.hs 16 | 17 | soln-json: soln-json.hs 18 | $(HC) $(HCFLAGS) --make soln-json.hs 19 | 20 | clean: 21 | -rm -f *.hi *.o soln soln-aeson soln-json 22 | -------------------------------------------------------------------------------- /12/README.md: -------------------------------------------------------------------------------- 1 | Part A of this problem looks like JSON is a red herring. You 2 | can simply strip out all the things that look like numbers 3 | and add them up. Sadly, after doing so, you find that Part B 4 | pretty much requires parsing the JSON anyhow. 5 | 6 | I learned how to use three JSON parsers for this. The 7 | standard is Aeson, which worked fine in the end, but 8 | initially I had trouble figuring out how to use it in this 9 | simple "just give me a parse without a schema" problem. I 10 | got things working with Yocto, then Json, then Aeson. It 11 | turned out to be just a few lines of code in each case, but 12 | they were hard to get to because of weak documentation and 13 | my weak understanding of Haskell. 14 | 15 | I used a recursive solution: using a built-in parser for 16 | trees would be more appropriate, but I didn't have anything 17 | lying around that I could figure out how to work with the 18 | JSON datatypes I had. 19 | 20 | One interesting problem that all solutions shared is what to 21 | do with the JSON specification's loose notion of numbers. 22 | Technically, all JSON numbers are rationals, represented in 23 | decimal format. The various Haskells interpret this in a 24 | variety of ways, one of which is not to split up 'Integer' 25 | and non-'Integer' numbers in the JSON, nor to just represent 26 | the rationals as 'Double'. In the end, I just chose to 27 | convert sums to double and print them at the end. 28 | -------------------------------------------------------------------------------- /12/findnumbers.sh: -------------------------------------------------------------------------------- 1 | # Copyright © 2015 Bart Massey 2 | # [This program is licensed under the "MIT License"] 3 | # Please see the file COPYING in the source 4 | # distribution of this software for license terms. 5 | -------------------------------------------------------------------------------- /12/problem-a.md: -------------------------------------------------------------------------------- 1 | Santa's Accounting-Elves need help balancing the books after 2 | a recent order. Unfortunately, their accounting software 3 | uses a peculiar storage format. That's where you come in. 4 | 5 | They have a JSON document which contains a variety of 6 | things: arrays (`[1,2,3]`), objects (`{"a":1, "b":2}`), 7 | numbers, and strings. Your first job is to simply find all 8 | of the numbers throughout the document and add them 9 | together. 10 | 11 | For example: 12 | 13 | * `[1,2,3]` and {"a":2,"b":4} both have a sum of 6. 14 | 15 | * `\[\[\[3]]]` and `{"a":{"b":4},"c":-1}` both have a sum of 3. 16 | 17 | * `{"a":[-1,1]}` and `[-1,{"a":1}]` both have a sum of 0. 18 | 19 | * `[]` and `{}` both have a sum of 0. 20 | 21 | You will not encounter any strings containing numbers. 22 | 23 | What is the sum of all numbers in the document? 24 | 25 | -------------------------------------------------------------------------------- /12/problem-b.md: -------------------------------------------------------------------------------- 1 | Uh oh - the Accounting-Elves have realized that they 2 | double-counted everything red. 3 | 4 | Ignore any object (and all of its children) which has any 5 | property with the value "red". Do this only for objects 6 | (`{...}`), not arrays (`[...]`). 7 | 8 | * `[1,2,3]` still has a sum of 6. 9 | * `[1,{"c":"red","b":2},3]` now has a sum of 4, because the 10 | middle object is ignored. 11 | * `{"d":"red","e":[1,2,3,4],"f":5}` now has a sum of 0, 12 | because the entire structure is ignored. 13 | * `[1,"red",5]` has a sum of 6, because "red" in an array 14 | has no effect. 15 | -------------------------------------------------------------------------------- /12/soln-aeson.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | {-# LANGUAGE OverloadedStrings #-} 7 | 8 | import Data.Aeson 9 | import qualified Data.ByteString.Lazy.Char8 as B 10 | import qualified Data.HashMap.Strict as M 11 | import qualified Data.Vector as V 12 | 13 | import Soln 14 | 15 | -- | Print the sum of all the numbers except optionally 16 | -- not the numbers in red objects. 17 | soln :: Bool -> String -> IO () 18 | soln ignoreRed stuff = do 19 | print $ addJson $ fromJust $ decode $ B.pack stuff 20 | where 21 | addJson (Number n) = n 22 | addJson (Array a) = sum $ map addJson $ V.toList a 23 | addJson (Object m) | ignoreRed && String "red" `elem` M.elems m = 0 24 | addJson (Object m) = sum $ map addJson $ M.elems m 25 | addJson _ = 0 26 | 27 | solna :: String -> IO () 28 | solna stuff = soln False stuff 29 | 30 | solnb :: String -> IO () 31 | solnb stuff = soln True stuff 32 | 33 | main :: IO () 34 | main = makeMain solna solnb 35 | -------------------------------------------------------------------------------- /12/soln-json.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | 7 | import Numeric 8 | import Text.JSON 9 | 10 | import Soln 11 | 12 | -- | Print the sum of all the numbers except optionally 13 | -- not the numbers in red objects. 14 | soln :: Bool -> String -> IO () 15 | soln ignoreRed stuff = do 16 | print $ (fromRat $ addJson $ fromOk $ decode stuff :: Double) 17 | where 18 | fromOk (Ok v) = v 19 | fromOk _ = error "not ok" 20 | addJson (JSRational _ n) = n 21 | addJson (JSArray vs) = sum $ map addJson vs 22 | addJson (JSObject m) 23 | | ignoreRed && hasRed = 0 24 | | otherwise = sum $ map addJson values 25 | where 26 | values = map snd (fromJSObject m) 27 | hasRed = JSString (toJSString "red") `elem` values 28 | addJson _ = 0 29 | 30 | solna :: String -> IO () 31 | solna stuff = soln False stuff 32 | 33 | solnb :: String -> IO () 34 | solnb stuff = soln True stuff 35 | 36 | main :: IO () 37 | main = makeMain solna solnb 38 | -------------------------------------------------------------------------------- /12/soln.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | 7 | import qualified Data.Map.Strict as M 8 | import Numeric 9 | import Text.JSON.Yocto 10 | 11 | import Soln 12 | 13 | -- | Print the sum of all the numbers except optionally 14 | -- not the numbers in red objects. 15 | soln :: Bool -> String -> IO () 16 | soln ignoreRed stuff = do 17 | print $ (fromRat $ addJson $ decode stuff :: Double) 18 | where 19 | addJson (Number n) = n 20 | addJson (Array vs) = sum $ map addJson vs 21 | addJson (Object m) | ignoreRed && String "red" `elem` M.elems m = 0 22 | addJson (Object m) = sum $ map addJson $ M.elems m 23 | addJson _ = 0 24 | 25 | solna :: String -> IO () 26 | solna stuff = soln False stuff 27 | 28 | solnb :: String -> IO () 29 | solnb stuff = soln True stuff 30 | 31 | main :: IO () 32 | main = makeMain solna solnb 33 | -------------------------------------------------------------------------------- /13/Makefile: -------------------------------------------------------------------------------- 1 | ../Soln/Makefile.template -------------------------------------------------------------------------------- /13/README.md: -------------------------------------------------------------------------------- 1 | Basically an exercise in parsing, plus another brute-force 2 | evaluator. 3 | 4 | The Haskell is pretty boring. 5 | -------------------------------------------------------------------------------- /13/input.txt: -------------------------------------------------------------------------------- 1 | Alice would gain 54 happiness units by sitting next to Bob. 2 | Alice would lose 81 happiness units by sitting next to Carol. 3 | Alice would lose 42 happiness units by sitting next to David. 4 | Alice would gain 89 happiness units by sitting next to Eric. 5 | Alice would lose 89 happiness units by sitting next to Frank. 6 | Alice would gain 97 happiness units by sitting next to George. 7 | Alice would lose 94 happiness units by sitting next to Mallory. 8 | Bob would gain 3 happiness units by sitting next to Alice. 9 | Bob would lose 70 happiness units by sitting next to Carol. 10 | Bob would lose 31 happiness units by sitting next to David. 11 | Bob would gain 72 happiness units by sitting next to Eric. 12 | Bob would lose 25 happiness units by sitting next to Frank. 13 | Bob would lose 95 happiness units by sitting next to George. 14 | Bob would gain 11 happiness units by sitting next to Mallory. 15 | Carol would lose 83 happiness units by sitting next to Alice. 16 | Carol would gain 8 happiness units by sitting next to Bob. 17 | Carol would gain 35 happiness units by sitting next to David. 18 | Carol would gain 10 happiness units by sitting next to Eric. 19 | Carol would gain 61 happiness units by sitting next to Frank. 20 | Carol would gain 10 happiness units by sitting next to George. 21 | Carol would gain 29 happiness units by sitting next to Mallory. 22 | David would gain 67 happiness units by sitting next to Alice. 23 | David would gain 25 happiness units by sitting next to Bob. 24 | David would gain 48 happiness units by sitting next to Carol. 25 | David would lose 65 happiness units by sitting next to Eric. 26 | David would gain 8 happiness units by sitting next to Frank. 27 | David would gain 84 happiness units by sitting next to George. 28 | David would gain 9 happiness units by sitting next to Mallory. 29 | Eric would lose 51 happiness units by sitting next to Alice. 30 | Eric would lose 39 happiness units by sitting next to Bob. 31 | Eric would gain 84 happiness units by sitting next to Carol. 32 | Eric would lose 98 happiness units by sitting next to David. 33 | Eric would lose 20 happiness units by sitting next to Frank. 34 | Eric would lose 6 happiness units by sitting next to George. 35 | Eric would gain 60 happiness units by sitting next to Mallory. 36 | Frank would gain 51 happiness units by sitting next to Alice. 37 | Frank would gain 79 happiness units by sitting next to Bob. 38 | Frank would gain 88 happiness units by sitting next to Carol. 39 | Frank would gain 33 happiness units by sitting next to David. 40 | Frank would gain 43 happiness units by sitting next to Eric. 41 | Frank would gain 77 happiness units by sitting next to George. 42 | Frank would lose 3 happiness units by sitting next to Mallory. 43 | George would lose 14 happiness units by sitting next to Alice. 44 | George would lose 12 happiness units by sitting next to Bob. 45 | George would lose 52 happiness units by sitting next to Carol. 46 | George would gain 14 happiness units by sitting next to David. 47 | George would lose 62 happiness units by sitting next to Eric. 48 | George would lose 18 happiness units by sitting next to Frank. 49 | George would lose 17 happiness units by sitting next to Mallory. 50 | Mallory would lose 36 happiness units by sitting next to Alice. 51 | Mallory would gain 76 happiness units by sitting next to Bob. 52 | Mallory would lose 34 happiness units by sitting next to Carol. 53 | Mallory would gain 37 happiness units by sitting next to David. 54 | Mallory would gain 40 happiness units by sitting next to Eric. 55 | Mallory would gain 18 happiness units by sitting next to Frank. 56 | Mallory would gain 7 happiness units by sitting next to George. 57 | -------------------------------------------------------------------------------- /13/problem-a.md: -------------------------------------------------------------------------------- 1 | In years past, the holiday feast with your family hasn't 2 | gone so well. Not everyone gets along! This year, you 3 | resolve, will be different. You're going to find the optimal 4 | seating arrangement and avoid all those awkward 5 | conversations. 6 | 7 | You start by writing up a list of everyone invited and the 8 | amount their happiness would increase or decrease if they 9 | were to find themselves sitting next to each other 10 | person. You have a circular table that will be just big 11 | enough to fit everyone comfortably, and so each person will 12 | have exactly two neighbors. 13 | 14 | For example, suppose you have only four attendees planned, 15 | and you calculate their potential happiness as follows: 16 | 17 | Alice would gain 54 happiness units by sitting next to Bob. 18 | Alice would lose 79 happiness units by sitting next to Carol. 19 | Alice would lose 2 happiness units by sitting next to David. 20 | Bob would gain 83 happiness units by sitting next to Alice. 21 | Bob would lose 7 happiness units by sitting next to Carol. 22 | Bob would lose 63 happiness units by sitting next to David. 23 | Carol would lose 62 happiness units by sitting next to Alice. 24 | Carol would gain 60 happiness units by sitting next to Bob. 25 | Carol would gain 55 happiness units by sitting next to David. 26 | David would gain 46 happiness units by sitting next to Alice. 27 | David would lose 7 happiness units by sitting next to Bob. 28 | David would gain 41 happiness units by sitting next to Carol. 29 | 30 | Then, if you seat Alice next to David, Alice would lose 2 31 | happiness units (because David talks so much), but David 32 | would gain 46 happiness units (because Alice is such a good 33 | listener), for a total change of 44. 34 | 35 | If you continue around the table, you could then seat Bob 36 | next to Alice (Bob gains 83, Alice gains 54). Finally, seat 37 | Carol, who sits next to Bob (Carol gains 60, Bob loses 7) 38 | and David (Carol gains 55, David gains 41). The arrangement 39 | looks like this: 40 | 41 | +41 +46 42 | +55 David -2 43 | Carol Alice 44 | +60 Bob +54 45 | -7 +83 46 | 47 | After trying every other seating arrangement in this 48 | hypothetical scenario, you find that this one is the most 49 | optimal, with a total change in happiness of 330. 50 | 51 | What is the total change in happiness for the optimal 52 | seating arrangement of the actual guest list? 53 | -------------------------------------------------------------------------------- /13/problem-b.md: -------------------------------------------------------------------------------- 1 | In all the commotion, you realize that you forgot to seat 2 | yourself. At this point, you're pretty apathetic toward the 3 | whole thing, and your happiness wouldn't really go up or 4 | down regardless of who you sit next to. You assume everyone 5 | else would be just as ambivalent about sitting next to you, 6 | too. 7 | 8 | So, add yourself to the list, and give all happiness 9 | relationships that involve you a score of 0. 10 | 11 | What is the total change in happiness for the optimal 12 | seating arrangement that actually includes yourself? 13 | 14 | -------------------------------------------------------------------------------- /13/soln.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | 7 | import qualified Data.Map.Strict as M 8 | import qualified Data.Set as S 9 | 10 | import Soln 11 | 12 | -- | Map (directed) pairs of people to score. 13 | type PrefMap = M.Map (String, String) Int 14 | 15 | 16 | -- | Parse an individual preference. 17 | parsePref :: [String] -> ((String, String), Int) 18 | parsePref [p1, "would", adj, v, "happiness", "units", 19 | "by", "sitting", "next", "to", p2dot ] = 20 | ((p1, init p2dot), gainLose adj (read v)) 21 | where 22 | gainLose "lose" n = -n 23 | gainLose "gain" n = n 24 | gainLose _ _ = error "bad adjective" 25 | parsePref _ = error "bad preference" 26 | 27 | -- | Parse a preference list into a 'PrefMap'. 28 | parsePrefs :: String -> PrefMap 29 | parsePrefs stuff = M.fromList $ map (parsePref . words) $ lines stuff 30 | 31 | -- | List of all people in the map. 32 | findPeople :: [(String, String)] -> [String] 33 | findPeople pairs = 34 | S.toList $ foldl' persons S.empty pairs 35 | where 36 | persons s (p1, p2) = S.insert p1 $ S.insert p2 s 37 | 38 | -- | Get parse of preferences in final form. 39 | processPrefs :: String -> (PrefMap, [String]) 40 | processPrefs stuff = 41 | let prefMap = parsePrefs stuff in 42 | (prefMap, findPeople $ M.keys prefMap) 43 | 44 | -- | Strategy: Brute force. 45 | bestScore :: PrefMap -> [String] -> Int 46 | bestScore _ [] = 0 47 | bestScore m xs = 48 | maximum $ map scorePerm $ permutations xs 49 | where 50 | scorePerm [] = 0 51 | scorePerm ps@(p : _) = 52 | sum $ map scorePair $ concatMap permutations $ tiles 2 1 $ ps ++ [p] 53 | where 54 | scorePair [p1, p2] = fromJust $ M.lookup (p1, p2) m 55 | scorePair _ = error "bad tile" 56 | 57 | solna :: String -> IO () 58 | solna stuff = do 59 | let (prefMap, people) = processPrefs stuff 60 | print $ bestScore prefMap people 61 | 62 | -- | Strategy: Hack up the input, then go. 63 | solnb :: String -> IO () 64 | solnb stuff = do 65 | let (prefMap, people) = processPrefs stuff 66 | let prefMap' = foldl' addMe prefMap people 67 | where 68 | addMe m p = 69 | M.insert ("me", p) 0 $ M.insert (p, "me") 0 m 70 | let people' = "me" : people 71 | print $ bestScore prefMap' people' 72 | 73 | main :: IO () 74 | main = makeMain solna solnb 75 | -------------------------------------------------------------------------------- /14/Makefile: -------------------------------------------------------------------------------- 1 | ../Soln/Makefile.template -------------------------------------------------------------------------------- /14/README.md: -------------------------------------------------------------------------------- 1 | There are probably more efficient plans than making a trace 2 | of the position of each reindeer at each second of the race. 3 | However, linear algebra is hard. 4 | 5 | The Haskell code uses `iterate` to generate an "infinite" 6 | trace for each reindeer. `transpose` is used twice in the 7 | Part B scoring: once to get all the positions at a given 8 | second, and then again to turn it back into a score trace. 9 | -------------------------------------------------------------------------------- /14/input.txt: -------------------------------------------------------------------------------- 1 | Rudolph can fly 22 km/s for 8 seconds, but then must rest for 165 seconds. 2 | Cupid can fly 8 km/s for 17 seconds, but then must rest for 114 seconds. 3 | Prancer can fly 18 km/s for 6 seconds, but then must rest for 103 seconds. 4 | Donner can fly 25 km/s for 6 seconds, but then must rest for 145 seconds. 5 | Dasher can fly 11 km/s for 12 seconds, but then must rest for 125 seconds. 6 | Comet can fly 21 km/s for 6 seconds, but then must rest for 121 seconds. 7 | Blitzen can fly 18 km/s for 3 seconds, but then must rest for 50 seconds. 8 | Vixen can fly 20 km/s for 4 seconds, but then must rest for 75 seconds. 9 | Dancer can fly 7 km/s for 20 seconds, but then must rest for 119 seconds. 10 | -------------------------------------------------------------------------------- /14/problem-a.md: -------------------------------------------------------------------------------- 1 | This year is the Reindeer Olympics! Reindeer can fly at high 2 | speeds, but must rest occasionally to recover their 3 | energy. Santa would like to know which of his reindeer is 4 | fastest, and so he has them race. 5 | 6 | Reindeer can only either be flying (always at their top 7 | speed) or resting (not moving at all), and always spend 8 | whole seconds in either state. 9 | 10 | For example, suppose you have the following Reindeer: 11 | 12 | * Comet can fly 14 km/s for 10 seconds, but then must rest for 127 seconds. 13 | * Dancer can fly 16 km/s for 11 seconds, but then must rest for 162 seconds. 14 | 15 | The race proceeds as follows: 16 | 17 | * After one second, Comet has gone 14 km, while Dancer has 18 | gone 16 km. 19 | 20 | * After ten seconds, Comet has gone 140 km, while Dancer has 21 | gone 160 km. 22 | 23 | * On the eleventh second, Comet begins resting (staying at 24 | 140 km), and Dancer continues on for a total distance of 25 | 176 km. 26 | 27 | * On the 12th second, both reindeer are resting. 28 | 29 | * They continue to rest until the 138th second, when Comet 30 | flies for another ten seconds. 31 | 32 | * On the 174th second, Dancer flies for another 11 seconds. 33 | 34 | In this example, after the 1000th second, both reindeer are 35 | resting, and Comet is in the lead at 1120 km (poor Dancer 36 | has only gotten 1056 km by that point). So, in this 37 | situation, Comet would win (if the race ended at 1000 38 | seconds). 39 | 40 | Given the descriptions of each reindeer (in your puzzle 41 | input), after exactly 2503 seconds, what distance has the 42 | winning reindeer traveled? 43 | -------------------------------------------------------------------------------- /14/problem-b.md: -------------------------------------------------------------------------------- 1 | Seeing how reindeer move in bursts, Santa decides he's not 2 | pleased with the old scoring system. 3 | 4 | Instead, at the end of each second, he awards one point to 5 | the reindeer currently in the lead. (If there are multiple 6 | reindeer tied for the lead, they each get one point.) He 7 | keeps the traditional 2503 second time limit, of course, as 8 | doing otherwise would be entirely ridiculous. 9 | 10 | Given the example reindeer from above, after the first 11 | second, Dancer is in the lead and gets one point. He stays 12 | in the lead until several seconds into Comet's second burst: 13 | after the 140th second, Comet pulls into the lead and gets 14 | his first point. Of course, since Dancer had been in the 15 | lead for the 139 seconds before that, he has accumulated 139 16 | points by the 140th second. 17 | 18 | After the 1000th second, Dancer has accumulated 689 points, 19 | while poor Comet, our old champion, only has 312. So, with 20 | the new scoring system, Dancer would win (if the race ended 21 | at 1000 seconds). 22 | 23 | Again given the descriptions of each reindeer (in your 24 | puzzle input), after exactly 2503 seconds, how many points 25 | does the winning reindeer have? 26 | -------------------------------------------------------------------------------- /14/soln.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | 7 | import Soln 8 | 9 | -- | For some reason, the race is over after 2503 seconds. 10 | endTime :: Int 11 | endTime = 2503 12 | 13 | -- | Description of a reindeer. 14 | data Reindeer = Reindeer { 15 | velocity, flightTime, restTime :: Int } 16 | 17 | -- | Read a reindeer description. 18 | parseReindeer :: [String] -> Reindeer 19 | parseReindeer [ _, "can", "fly", vs, "km/s", "for", tfs, "seconds,", 20 | "but", "then", "must", "rest", "for", trs, "seconds." ] = 21 | Reindeer (read vs) (read tfs) (read trs) 22 | parseReindeer _ = error "bad reindeer" 23 | 24 | -- | Produce a trace that shows the given reindeer's position 25 | -- at every race second. 26 | runReindeer :: Reindeer -> [Int] 27 | runReindeer reindeer = 28 | concatMap makeLeg $ iterate legEnd 0 29 | where 30 | -- | Compute the position of the reindeer at the end of 31 | -- the leg. 32 | legEnd :: Int -> Int 33 | legEnd start = 34 | start + flightTime reindeer * velocity reindeer 35 | -- | Compute the position of the reindeer at each second 36 | -- of the leg. 37 | makeLeg :: Int -> [Int] 38 | makeLeg start = 39 | let end = legEnd start in 40 | [start, start + velocity reindeer .. 41 | end - velocity reindeer] ++ 42 | replicate (restTime reindeer) end 43 | 44 | -- | Produce traces for all reindeer in the race. 45 | processReindeers:: String -> [[Int]] 46 | processReindeers stuff = 47 | map (runReindeer . parseReindeer . words) $ lines stuff 48 | 49 | -- | Strategy: Find a largest distance at end of race. 50 | solna :: String -> IO () 51 | solna stuff = do 52 | print $ maximum $ map (!! endTime) $ processReindeers stuff 53 | 54 | -- | Given the current position of each reindeer, 55 | -- compute the score of each reindeer. 56 | scorePosns :: [Int] -> [Int] 57 | scorePosns posns = 58 | map (deltaMax (maximum posns)) posns 59 | where 60 | deltaMax maxPosn posn 61 | | posn == maxPosn = 1 62 | | otherwise = 0 63 | 64 | -- | Strategy: Score each reindeer at each second, then 65 | -- sum each reindeer's scores over the race to get an 66 | -- overall score for that reindeer. Return the maximum 67 | -- total score. 68 | solnb :: String -> IO () 69 | solnb stuff = do 70 | print $ maximum $ map sum $ transpose $ take endTime $ tail $ 71 | map scorePosns $ transpose $ processReindeers stuff 72 | 73 | main :: IO () 74 | main = makeMain solna solnb 75 | -------------------------------------------------------------------------------- /15/.gitignore: -------------------------------------------------------------------------------- 1 | /best-recipe 2 | -------------------------------------------------------------------------------- /15/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright © 2015 Bart Massey 2 | # [This program is licensed under the "MIT License"] 3 | # Please see the file COPYING in the source 4 | # distribution of this software for license terms. 5 | 6 | HC = ghc 7 | HCFLAGS = -i../Soln -Wall -O2 8 | 9 | soln: soln.hs 10 | $(HC) $(HCFLAGS) --make soln.hs 11 | 12 | best-recipe: best-recipe.hs 13 | $(HC) $(HCFLAGS) --make best-recipe.hs 14 | 15 | clean: 16 | -rm -f soln.hi soln.o soln 17 | -rm -f best-recipe.hi best-recipe.o best-recipe 18 | ( cd ../Soln && make clean ) 19 | -------------------------------------------------------------------------------- /15/README.md: -------------------------------------------------------------------------------- 1 | This problem suggests the use of linear programming, which 2 | is the traditional way to solve this kind of thing. But the 3 | nonlinearity of negative ingredient sums makes that hard, 4 | plus it's a lot of work. So brute force again. 5 | 6 | The interesting part of the Haskell is the thing that 7 | computes all the possible ingredient combinations. 8 | 9 | I've included a little auxiliary program that shows the 10 | actual recipes. 11 | -------------------------------------------------------------------------------- /15/best-recipe.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | 7 | import Soln 8 | 9 | sumsUpTo :: Int -> Int -> [[Int]] 10 | sumsUpTo k n = 11 | iterate extendSums [[]] !! n 12 | where 13 | extendSums :: [[Int]] -> [[Int]] 14 | extendSums partials = 15 | concatMap extendSum partials 16 | where 17 | extendSum partial = 18 | map (: partial) [0 .. k - sum partial] 19 | 20 | sumsTo :: Int -> Int -> [[Int]] 21 | sumsTo 0 0 = [[]] 22 | sumsTo k 0 | k > 0 = [] 23 | sumsTo k n | k >= 0 && n > 0 = 24 | map completeSum $ sumsUpTo k (n - 1) 25 | where 26 | completeSum partial = 27 | (k - sum partial) : partial 28 | sumsTo _ _ = error "negative quantities" 29 | 30 | parseIngredient :: [String] -> [Int] 31 | parseIngredient (_ : properties) = 32 | map (parseProperty . fst) $ filter (odd . snd) $ 33 | zip properties [(0 :: Int) ..] 34 | where 35 | parseProperty p | last p == ',' = parseProperty $ init p 36 | parseProperty p = read p 37 | parseIngredient _ = error "bad ingredient" 38 | 39 | parseIngredients :: String -> [[Int]] 40 | parseIngredients stuff = 41 | map (parseIngredient . words) $ lines stuff 42 | 43 | soln :: ([[Int]] -> [Int] -> Maybe Int) -> String -> [Int] 44 | soln scorer stuff = 45 | maximumBy (comparing (scorer ingredients)) $ 46 | sumsTo 100 $ length ingredients 47 | where 48 | ingredients = parseIngredients stuff 49 | 50 | dotProduct :: [Int] -> [Int] -> Int 51 | dotProduct a b = sum $ zipWith (*) a b 52 | 53 | scoreRecipe :: [[Int]] -> [Int] -> Maybe Int 54 | scoreRecipe ingredients r = 55 | Just $ product $ map ((`max` 0) . dotProduct r) $ 56 | transpose $ map init ingredients 57 | 58 | solna :: String -> IO () 59 | solna stuff = do 60 | print $ soln scoreRecipe stuff 61 | 62 | scoreSpecialRecipe :: [[Int]] -> [Int] -> Maybe Int 63 | scoreSpecialRecipe ingredients r 64 | | totalCalories == 500 = scoreRecipe ingredients r 65 | | otherwise = Nothing 66 | where 67 | totalCalories = 68 | dotProduct r $ map last ingredients 69 | 70 | solnb :: String -> IO () 71 | solnb stuff = do 72 | print $ soln scoreSpecialRecipe stuff 73 | 74 | main :: IO () 75 | main = makeMain solna solnb 76 | -------------------------------------------------------------------------------- /15/input.txt: -------------------------------------------------------------------------------- 1 | Sugar: capacity 3, durability 0, flavor 0, texture -3, calories 2 2 | Sprinkles: capacity -3, durability 3, flavor 0, texture 0, calories 9 3 | Candy: capacity -1, durability 0, flavor 4, texture 0, calories 1 4 | Chocolate: capacity 0, durability 0, flavor -2, texture 2, calories 8 5 | -------------------------------------------------------------------------------- /15/problem-a.md: -------------------------------------------------------------------------------- 1 | Today, you set out on the task of perfecting your 2 | milk-dunking cookie recipe. All you have to do is find the 3 | right balance of ingredients. 4 | 5 | Your recipe leaves room for exactly 100 teaspoons of 6 | ingredients. You make a list of the remaining ingredients 7 | you could use to finish the recipe (your puzzle input) and 8 | their properties per teaspoon: 9 | 10 | * capacity (how well it helps the cookie absorb milk) 11 | * durability (how well it keeps the cookie intact when full 12 | of milk) 13 | * flavor (how tasty it makes the cookie) 14 | * texture (how it improves the feel of the cookie) 15 | * calories (how many calories it adds to the cookie) 16 | 17 | You can only measure ingredients in whole-teaspoon amounts 18 | accurately, and you have to be accurate so you can reproduce 19 | your results in the future. The total score of a cookie can 20 | be found by adding up each of the properties (negative 21 | totals become 0) and then multiplying together everything 22 | except calories. 23 | 24 | For instance, suppose you have these two ingredients: 25 | 26 | * Butterscotch: capacity -1, durability -2, flavor 6, 27 | texture 3, calories 8 28 | * Cinnamon: capacity 2, durability 3, flavor -2, texture -1, 29 | calories 3 30 | 31 | Then, choosing to use 44 teaspoons of butterscotch and 56 32 | teaspoons of cinnamon (because the amounts of each 33 | ingredient must add up to 100) would result in a cookie with 34 | the following properties: 35 | 36 | * A capacity of 44\*-1 + 56\*2 = 68 37 | * A durability of 44\*-2 + 56\*3 = 80 38 | * A flavor of 44\*6 + 56\*-2 = 152 39 | * A texture of 44\*3 + 56\*-1 = 76 40 | 41 | Multiplying these together (68 \* 80 \* 152 \* 76, ignoring 42 | calories for now) results in a total score of 62842880, 43 | which happens to be the best score possible given these 44 | ingredients. If any properties had produced a negative 45 | total, it would have instead become zero, causing the whole 46 | score to multiply to zero. 47 | 48 | Given the ingredients in your kitchen and their properties, 49 | what is the total score of the highest-scoring cookie you 50 | can make? 51 | -------------------------------------------------------------------------------- /15/problem-b.md: -------------------------------------------------------------------------------- 1 | Your cookie recipe becomes wildly popular! Someone asks if 2 | you can make another recipe that has exactly 500 calories 3 | per cookie (so they can use it as a meal replacement). Keep 4 | the rest of your award-winning process the same (100 5 | teaspoons, same ingredients, same scoring system). 6 | 7 | For example, given the ingredients above, if you had instead 8 | selected 40 teaspoons of butterscotch and 60 teaspoons of 9 | cinnamon (which still adds to 100), the total calorie count 10 | would be 40\*8 + 60\*3 = 500. The total score would go down, 11 | though: only 57600000, the best you can do in such trying 12 | circumstances. 13 | 14 | Given the ingredients in your kitchen and their properties, 15 | what is the total score of the highest-scoring cookie you 16 | can make with a calorie total of 500? 17 | 18 | -------------------------------------------------------------------------------- /15/soln.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | 7 | import Soln 8 | 9 | -- | Make a list of all the ways that a sum 10 | -- less than or equal to k can be achieved with n items, 11 | -- including sums involving 0. 12 | sumsUpTo :: Int -> Int -> [[Int]] 13 | sumsUpTo k n = 14 | iterate extendSums [[]] !! n 15 | where 16 | extendSums :: [[Int]] -> [[Int]] 17 | extendSums partials = 18 | concatMap extendSum partials 19 | where 20 | extendSum partial = 21 | map (: partial) [0 .. k - sum partial] 22 | 23 | -- | Make a list of all the ways that a sum of 24 | -- k can be achieved with n items, including 25 | -- sums involving 0. 26 | sumsTo :: Int -> Int -> [[Int]] 27 | sumsTo 0 0 = [[]] 28 | sumsTo k 0 | k > 0 = [] 29 | sumsTo k n | k >= 0 && n > 0 = 30 | map completeSum $ sumsUpTo k (n - 1) 31 | where 32 | completeSum partial = 33 | (k - sum partial) : partial 34 | sumsTo _ _ = error "negative quantities" 35 | 36 | -- | Given a comma-separated string description of an 37 | -- ingredient, produce a list of attributes 38 | parseIngredient :: [String] -> [Int] 39 | parseIngredient (_ : properties) = 40 | map (parseProperty . fst) $ filter (odd . snd) $ 41 | zip properties [(0 :: Int) ..] 42 | where 43 | parseProperty p | last p == ',' = parseProperty $ init p 44 | parseProperty p = read p 45 | parseIngredient _ = error "bad ingredient" 46 | 47 | -- | Parse all the ingredients. 48 | parseIngredients :: String -> [[Int]] 49 | parseIngredients stuff = 50 | map (parseIngredient . words) $ lines stuff 51 | 52 | -- | Strategy: Try all possible combinations of 53 | -- ingredients and pick the best-scoring. 54 | soln :: ([[Int]] -> [Int] -> Maybe Int) -> String -> Int 55 | soln scorer stuff = 56 | let ingredients = parseIngredients stuff 57 | scores = 58 | map (scorer ingredients) $ sumsTo 100 $ length ingredients 59 | in 60 | case maximum scores of 61 | Just bestScore -> bestScore 62 | Nothing -> error "no solution" 63 | 64 | -- | Compute the dot product of two lists of integers. 65 | dotProduct :: [Int] -> [Int] -> Int 66 | dotProduct a b = sum $ zipWith (*) a b 67 | 68 | -- | The score of a recipe is the product of the 69 | -- non-negative ingredient sums. This collapses to 0 70 | -- when any ingredient has a negative score. Calories 71 | -- are assumed to be last and are ignored. 72 | scoreRecipe :: [[Int]] -> [Int] -> Maybe Int 73 | scoreRecipe ingredients r = 74 | Just $ product $ map ((`max` 0) . dotProduct r) $ 75 | transpose $ map init ingredients 76 | 77 | solna :: String -> IO () 78 | solna stuff = do 79 | print $ soln scoreRecipe stuff 80 | 81 | -- | A special recipe is only scored if it has 82 | -- exactly 500 calories. 83 | scoreSpecialRecipe :: [[Int]] -> [Int] -> Maybe Int 84 | scoreSpecialRecipe ingredients r 85 | | totalCalories == 500 = scoreRecipe ingredients r 86 | | otherwise = Nothing 87 | where 88 | totalCalories = 89 | dotProduct r $ map last ingredients 90 | 91 | solnb :: String -> IO () 92 | solnb stuff = do 93 | print $ soln scoreSpecialRecipe stuff 94 | 95 | main :: IO () 96 | main = makeMain solna solnb 97 | -------------------------------------------------------------------------------- /16/Makefile: -------------------------------------------------------------------------------- 1 | ../Soln/Makefile.template -------------------------------------------------------------------------------- /16/README.md: -------------------------------------------------------------------------------- 1 | Interesting logic puzzle, although hard-coding the target 2 | aunt's description makes the problem a little weird. 3 | 4 | Haskell makes good use of lists of functions here, to 5 | evaluate the descriptors. Note the use of `<*>` in `soln` to 6 | avoid some `Maybe`-whacking. `soln` still seems overly 7 | complicated, though: probably can do something to make it 8 | cleaner. 9 | -------------------------------------------------------------------------------- /16/problem-a.md: -------------------------------------------------------------------------------- 1 | Your Aunt Sue has given you a wonderful gift, and you'd like 2 | to send her a thank you card. However, there's a small 3 | problem\: she signed it "From, Aunt Sue". 4 | 5 | You have 500 Aunts named "Sue". 6 | 7 | So, to avoid sending the card to the wrong person, you need 8 | to figure out which Aunt Sue (which you conveniently number 9 | 1 to 500, for sanity) gave you the gift. You open the 10 | present and, as luck would have it, good ol' Aunt Sue got 11 | you a My First Crime Scene Analysis Machine! Just what you 12 | wanted. Or needed, as the case may be. 13 | 14 | The My First Crime Scene Analysis Machine (MFCSAM for short) 15 | can detect a few specific compounds in a given sample, as 16 | well as how many distinct kinds of those compounds there 17 | are. According to the instructions, these are what the 18 | MFCSAM can detect: 19 | 20 | * children, by human DNA age analysis. 21 | * cats. It doesn't differentiate individual breeds. 22 | * Several seemingly random breeds of dog: samoyeds, 23 | pomeranians, akitas, and vizslas. 24 | * goldfish. No other kinds of fish. 25 | * trees, all in one group. 26 | * cars, presumably by exhaust or gasoline or something. 27 | * perfumes, which is handy, since many of your Aunts Sue 28 | wear a few kinds. 29 | 30 | In fact, many of your Aunts Sue have many of these. You put 31 | the wrapping from the gift into the MFCSAM. It beeps 32 | inquisitively at you a few times and then prints out a 33 | message on ticker tape: 34 | 35 | children: 3 36 | cats: 7 37 | samoyeds: 2 38 | pomeranians: 3 39 | akitas: 0 40 | vizslas: 0 41 | goldfish: 5 42 | trees: 3 43 | cars: 2 44 | perfumes: 1 45 | 46 | You make a list of the things you can remember about each 47 | Aunt Sue. Things missing from your list aren't zero - you 48 | simply don't remember the value. 49 | 50 | What is the number of the Sue that got you the gift? 51 | -------------------------------------------------------------------------------- /16/problem-b.md: -------------------------------------------------------------------------------- 1 | As you're about to send the thank you note, something in the 2 | MFCSAM's instructions catches your eye. Apparently, it has 3 | an outdated retroencabulator, and so the output from the 4 | machine isn't exact values - some of them indicate ranges. 5 | 6 | In particular, the cats and trees readings indicates that 7 | there are greater than that many (due to the unpredictable 8 | nuclear decay of cat dander and tree pollen), while the 9 | pomeranians and goldfish readings indicate that there are 10 | fewer than that many (due to the modial interaction of 11 | magnetoreluctance). 12 | 13 | What is the number of the real Aunt Sue? 14 | 15 | -------------------------------------------------------------------------------- /16/soln.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | 7 | import qualified Data.Map as M 8 | 9 | import Soln 10 | 11 | -- | Map from characteristic name to characteristic value. 12 | type Aunt = M.Map String Int 13 | 14 | -- | Map from characteristic name to a function that 15 | -- indicates whether the quantity of that ingredient is in 16 | -- the acceptable range. 17 | type AuntEval = M.Map String (Int -> Bool) 18 | 19 | -- | The aunt of Part A. 20 | targetAunt :: AuntEval 21 | targetAunt = M.fromList [ 22 | ("children:", (== 3)), 23 | ("cats:", (== 7)), 24 | ("samoyeds:", (== 2)), 25 | ("pomeranians:", (== 3)), 26 | ("akitas:", (== 0)), 27 | ("vizslas:", (== 0)), 28 | ("goldfish:", (== 5)), 29 | ("trees:", (== 3)), 30 | ("cars:", (== 2)), 31 | ("perfumes:", (== 1)) ] 32 | 33 | -- | The aunt of Part B. 34 | targetAunt' :: AuntEval 35 | targetAunt' = M.fromList [ 36 | ("children:", (== 3)), 37 | ("cats:", (> 7)), 38 | ("samoyeds:", (== 2)), 39 | ("pomeranians:", (< 3)), 40 | ("akitas:", (== 0)), 41 | ("vizslas:", (== 0)), 42 | ("goldfish:", (< 5)), 43 | ("trees:", (> 3)), 44 | ("cars:", (== 2)), 45 | ("perfumes:", (== 1)) ] 46 | 47 | -- | Parse an aunt description. 48 | parseAunt :: [String] -> (Int, Aunt) 49 | parseAunt ("Sue" : nStr : attrs) = 50 | (read $ init nStr, foldr parseAttrs M.empty $ tiles 2 2 attrs) 51 | where 52 | parseAttrs [k, vStr] m = 53 | M.insert k (read $ filter isDigit $ vStr) m 54 | parseAttrs _ _ = error "bad attr" 55 | parseAunt _ = error "bad aunt" 56 | 57 | -- | Get all the aunt descriptions. 58 | parseAunts :: String -> [(Int, Aunt)] 59 | parseAunts stuff = 60 | map (parseAunt . words) $ lines stuff 61 | 62 | -- | Strategy: Look for an aunt that matches 63 | -- the given target aunt. 64 | soln :: AuntEval -> String -> IO () 65 | soln aTargetAunt stuff = 66 | case lookup True $ map auntTest $ parseAunts stuff of 67 | Just a -> print a 68 | Nothing -> error "no matching aunt" 69 | where 70 | auntTest (num, desc) = 71 | (all attrMatch (M.keys desc), num) 72 | where 73 | attrMatch k = 74 | case M.lookup k aTargetAunt <*> M.lookup k desc of 75 | Just b -> b 76 | Nothing -> error "invalid aunt" 77 | 78 | solna :: String -> IO () 79 | solna stuff = do 80 | soln targetAunt stuff 81 | 82 | solnb :: String -> IO () 83 | solnb stuff = do 84 | soln targetAunt' stuff 85 | 86 | main :: IO () 87 | main = makeMain solna solnb 88 | -------------------------------------------------------------------------------- /17/Makefile: -------------------------------------------------------------------------------- 1 | ../Soln/Makefile.template -------------------------------------------------------------------------------- /17/README.md: -------------------------------------------------------------------------------- 1 | This problem is SUBSET-SUM, which is NP-complete in 2 | general. So as usual we just brute-force it. 3 | 4 | Haskell can essentially be used as a powerful pocket 5 | calculator here. Because of GHC's efficient compilation of 6 | these kinds of codes, we get near-instantaneous answers 7 | evaluating approximately one million possibilities. 8 | -------------------------------------------------------------------------------- /17/input.txt: -------------------------------------------------------------------------------- 1 | 33 2 | 14 3 | 18 4 | 20 5 | 45 6 | 35 7 | 16 8 | 35 9 | 1 10 | 13 11 | 18 12 | 13 13 | 50 14 | 44 15 | 48 16 | 6 17 | 24 18 | 41 19 | 30 20 | 42 21 | -------------------------------------------------------------------------------- /17/problem-a.md: -------------------------------------------------------------------------------- 1 | The elves bought too much eggnog again - 150 liters this 2 | time. To fit it all into your refrigerator, you'll need to 3 | move it into smaller containers. You take an inventory of 4 | the capacities of the available containers. 5 | 6 | For example, suppose you have containers of size 20, 15, 10, 7 | 5, and 5 liters. If you need to store 25 liters, there are 8 | four ways to do it: 9 | 10 | * 15 and 10 11 | * 20 and 5 (the first 5) 12 | * 20 and 5 (the second 5) 13 | * 15, 5, and 5 14 | 15 | Filling all containers entirely, how many different 16 | combinations of containers can exactly fit all 150 liters of 17 | eggnog? 18 | 19 | -------------------------------------------------------------------------------- /17/problem-b.md: -------------------------------------------------------------------------------- 1 | While playing with all the containers in the kitchen, 2 | another load of eggnog arrives! The shipping and receiving 3 | department is requesting as many containers as you can 4 | spare. 5 | 6 | Find the minimum number of containers that can exactly fit 7 | all 150 liters of eggnog. How many different ways can you 8 | fill that number of containers and still hold exactly 150 9 | litres? 10 | 11 | In the example above, the minimum number of containers was 12 | two. There were three ways to use that many containers, and 13 | so the answer there would be 3. 14 | 15 | -------------------------------------------------------------------------------- /17/soln.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | 7 | import Soln 8 | 9 | -- | Amount of eggnog to be stored. 10 | targetVolume :: Int 11 | targetVolume = 150 12 | 13 | -- | All ways the target volume can be achieved. 14 | solns :: String -> [[Int]] 15 | solns stuff = 16 | filter ((== targetVolume) . sum) $ subsequences $ map read $ lines stuff 17 | 18 | solna :: String -> IO () 19 | solna stuff = do 20 | print $ length $ solns stuff 21 | 22 | -- | Strategy: find the minimum number of containers, then 23 | -- count the number of fillings using that many. 24 | solnb :: String -> IO () 25 | solnb stuff = do 26 | let minContainers = minimum $ map length $ solns stuff 27 | print $ length $ filter ((== minContainers) . length) $ solns stuff 28 | 29 | main :: IO () 30 | main = makeMain solna solnb 31 | -------------------------------------------------------------------------------- /18/.gitignore: -------------------------------------------------------------------------------- 1 | /life 2 | -------------------------------------------------------------------------------- /18/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright © 2015 Bart Massey 2 | # [This program is licensed under the "MIT License"] 3 | # Please see the file COPYING in the source 4 | # distribution of this software for license terms. 5 | 6 | HC = ghc 7 | HCFLAGS = -i../Soln -Wall -O2 8 | 9 | soln: soln.hs 10 | $(HC) $(HCFLAGS) --make soln.hs 11 | 12 | life: life.hs 13 | $(HC) $(HCFLAGS) --make life.hs 14 | 15 | clean: 16 | -rm -f soln.hi soln.o soln life.hi life.o life 17 | -------------------------------------------------------------------------------- /18/README.md: -------------------------------------------------------------------------------- 1 | OK, Game of Life. Probably those who did not have an 2 | implementation lying around missed the leaderboard. 3 | 4 | This Haskell implementation uses `Data.Map` to store the 5 | lights. This is probably less efficient than using an array, 6 | but it's fast enough. Haskell arrays are also kind of a 7 | pain. Also, this solution allows avoiding the usual clipping 8 | problems at board edges altogether without padding the array 9 | due to the magic of `Maybe`. 10 | 11 | I've included a solution that shows Life playing out on a 12 | smaller board. It requires the `ansi-terminal` and 13 | `unbounded-delay` packages from Hackage, and gives a hint of 14 | what can be done with interactive real-time stuff in 15 | Haskell. 16 | -------------------------------------------------------------------------------- /18/life.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | 7 | import Control.Concurrent (threadDelay) 8 | import qualified Data.Map.Strict as M 9 | import System.Console.ANSI 10 | import System.IO (hFlush, stdout) 11 | 12 | import Soln 13 | 14 | data Light = LightOn | LightOff 15 | 16 | type LightMap = M.Map (Int, Int) Light 17 | 18 | nFrames :: Int 19 | nFrames = 100 20 | 21 | xSize, ySize :: Int 22 | xSize = 50 23 | ySize = 20 24 | 25 | frameRate :: Int 26 | frameRate = 10 27 | 28 | indices :: [(Int, Int)] 29 | indices = [(x, y) | y <- [1 .. ySize], x <- [1 .. xSize]] 30 | 31 | readLights :: String -> LightMap 32 | readLights stuff = 33 | M.fromList $ zip indices $ map readLight $ concat $ lines stuff 34 | where 35 | readLight '#' = LightOn 36 | readLight '.' = LightOff 37 | readLight _ = error "bad light" 38 | 39 | countLights :: [(Int, Int)] -> LightMap -> Int 40 | countLights ixs m = 41 | sum $ map (lightValue . flip M.lookup m) ixs 42 | where 43 | lightValue (Just LightOn) = 1 44 | lightValue _ = 0 45 | 46 | playLife :: LightMap -> LightMap 47 | playLife m = 48 | M.mapWithKey updateLight m 49 | where 50 | updateLight (x, y) l 51 | | n < 2 = LightOff 52 | | n > 3 = LightOff 53 | | n == 3 = LightOn 54 | | n == 2 = l 55 | | otherwise = LightOff 56 | where 57 | n = countLights 58 | [ (x + dx, y + dy) | 59 | dy <- [-1 .. 1], 60 | dx <- [-1 .. 1], 61 | (dx, dy) /= (0, 0) ] m 62 | 63 | stickCorners :: LightMap -> LightMap 64 | stickCorners m0 = 65 | foldr stickCorner m0 [(x, y) | x <- [1, xSize], y <- [1, ySize]] 66 | where 67 | stickCorner xy m = M.insert xy LightOn m 68 | 69 | soln :: (LightMap -> LightMap) -> String -> IO () 70 | soln nextFrame stuff = do 71 | mapM_ showFrame $ take nFrames makeFrames 72 | where 73 | makeFrames = iterate nextFrame $ readLights stuff 74 | showFrame m = do 75 | clearScreen 76 | setCursorPosition 0 0 77 | putStr $ unlines $ transpose $ tiles ySize ySize $ 78 | map lightChar $ M.elems m 79 | hFlush stdout 80 | threadDelay $ 1000000 `div` frameRate 81 | where 82 | lightChar LightOn = '#' 83 | lightChar LightOff = '.' 84 | 85 | solna :: String -> IO () 86 | solna stuff = do 87 | soln playLife stuff 88 | 89 | solnb :: String -> IO () 90 | solnb stuff = do 91 | soln (stickCorners . playLife) stuff 92 | 93 | main :: IO () 94 | main = makeMain solna solnb 95 | -------------------------------------------------------------------------------- /18/problem-a.md: -------------------------------------------------------------------------------- 1 | After the million lights incident, the fire code has gotten 2 | stricter\: now, at most ten thousand lights are allowed. You 3 | arrange them in a 100x100 grid. 4 | 5 | Never one to let you down, Santa again mails you 6 | instructions on the ideal lighting configuration. With so 7 | few lights, he says, you'll have to resort to animation. 8 | 9 | Start by setting your lights to the included initial 10 | configuration (your puzzle input). A "`#`" means "on", and a 11 | "`.`" means "off". 12 | 13 | Then, animate your grid in steps, where each step decides 14 | the next configuration based on the current one. Each 15 | light's next state (either on or off) depends on its current 16 | state and the current states of the eight lights adjacent to 17 | it (including diagonals). Lights on the edge of the grid 18 | might have fewer than eight neighbors; the missing ones 19 | always count as "off". 20 | 21 | For example, in a simplified 6x6 grid, the light marked A 22 | has the neighbors numbered 1 through 8, and the light marked 23 | B, which is on an edge, only has the neighbors marked 1 24 | through 5: 25 | 26 | 1B5... 27 | 234... 28 | ...... 29 | ..123. 30 | ..8A4. 31 | ..765. 32 | 33 | The state a light should have next is based on its current 34 | state (on or off) plus the number of neighbors that are on: 35 | 36 | * A light which is on stays on when 2 or 3 neighbors are on, 37 | and turns off otherwise. 38 | 39 | * A light which is off turns on if exactly 3 neighbors are 40 | on, and stays off otherwise. 41 | 42 | All of the lights update simultaneously; they all consider 43 | the same current state before moving to the next. 44 | 45 | Here's a few steps from an example configuration of another 6x6 grid: 46 | 47 | Initial state: 48 | 49 | .#.#.# 50 | ...##. 51 | #....# 52 | ..#... 53 | #.#..# 54 | ####.. 55 | 56 | After 1 step: 57 | 58 | ..##.. 59 | ..##.# 60 | ...##. 61 | ...... 62 | #..... 63 | #.##.. 64 | 65 | After 2 steps: 66 | 67 | ..###. 68 | ...... 69 | ..###. 70 | ...... 71 | .#.... 72 | .#.... 73 | 74 | After 3 steps: 75 | 76 | ...#.. 77 | ...... 78 | ...#.. 79 | ..##.. 80 | ...... 81 | ...... 82 | 83 | After 4 steps: 84 | 85 | ...... 86 | ...... 87 | ..##.. 88 | ..##.. 89 | ...... 90 | ...... 91 | 92 | After 4 steps, this example has four lights on. 93 | 94 | In your grid of 100x100 lights, given your initial 95 | configuration, how many lights are on after 100 steps? 96 | 97 | -------------------------------------------------------------------------------- /18/problem-b.md: -------------------------------------------------------------------------------- 1 | You flip the instructions over; Santa goes on to point out 2 | that this is all just an implementation of Conway's Game of 3 | Life. At least, it was, until you notice that something's 4 | wrong with the grid of lights you bought\: four lights, one 5 | in each corner, are stuck on and can't be turned off. The 6 | example above will actually run like this\: 7 | 8 | Initial state: 9 | 10 | ##.#.# 11 | ...##. 12 | #....# 13 | ..#... 14 | #.#..# 15 | ####.# 16 | 17 | After 1 step: 18 | 19 | #.##.# 20 | ####.# 21 | ...##. 22 | ...... 23 | #...#. 24 | #.#### 25 | 26 | After 2 steps: 27 | 28 | #..#.# 29 | #....# 30 | .#.##. 31 | ...##. 32 | .#..## 33 | ##.### 34 | 35 | After 3 steps: 36 | 37 | #...## 38 | ####.# 39 | ..##.# 40 | ...... 41 | ##.... 42 | ####.# 43 | 44 | After 4 steps: 45 | 46 | #.#### 47 | #....# 48 | ...#.. 49 | .##... 50 | #..... 51 | #.#..# 52 | 53 | After 5 steps: 54 | 55 | ##.### 56 | .##..# 57 | .##... 58 | .##... 59 | #.#... 60 | ##...# 61 | 62 | After 5 steps, this example now has 17 lights on. 63 | 64 | In your grid of 100x100 lights, given your initial 65 | configuration, but with the four corners always in the on 66 | state, how many lights are on after 100 steps? 67 | 68 | -------------------------------------------------------------------------------- /18/soln.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | 7 | import qualified Data.Map.Strict as M 8 | 9 | import Soln 10 | 11 | -- | We use explicit light states for type safety. 12 | data Light = LightOn | LightOff 13 | 14 | -- | State of the board. 15 | type LightMap = M.Map (Int, Int) Light 16 | 17 | -- | Number of turns the run is supposed to be. 18 | nFrames :: Int 19 | nFrames = 100 20 | 21 | -- | Dimensions of the grid. 22 | xSize, ySize :: Int 23 | xSize = 100 24 | ySize = 100 25 | 26 | -- | List of all grid positions in row-major order. 27 | indices :: [(Int, Int)] 28 | indices = [(x, y) | y <- [1 .. ySize], x <- [1 .. xSize]] 29 | 30 | -- | Read a board state. 31 | readLights :: String -> LightMap 32 | readLights stuff = 33 | M.fromList $ zip indices $ map readLight $ concat $ lines stuff 34 | where 35 | readLight '#' = LightOn 36 | readLight '.' = LightOff 37 | readLight _ = error "bad light" 38 | 39 | 40 | -- | Count the number of turned-on lights in portion of 41 | -- the given indices that are actually in the light map. 42 | countLights :: [(Int, Int)] -> LightMap -> Int 43 | countLights ixs m = 44 | sum $ map (lightValue . flip M.lookup m) ixs 45 | where 46 | lightValue (Just LightOn) = 1 47 | lightValue _ = 0 48 | 49 | -- | One round of the Game of Life with Christmas lights. 50 | playLife :: LightMap -> LightMap 51 | playLife m = 52 | M.mapWithKey updateLight m 53 | where 54 | updateLight (x, y) l 55 | | n < 2 = LightOff 56 | | n > 3 = LightOff 57 | | n == 3 = LightOn 58 | | n == 2 = l 59 | | otherwise = LightOff 60 | where 61 | n = countLights 62 | [ (x + dx, y + dy) | 63 | dy <- [-1 .. 1], 64 | dx <- [-1 .. 1], 65 | (dx, dy) /= (0, 0) ] m 66 | 67 | -- | Turn any off corner lights back on. 68 | stickCorners :: LightMap -> LightMap 69 | stickCorners m0 = 70 | foldr stickCorner m0 [(x, y) | x <- [1, xSize], y <- [1, ySize]] 71 | where 72 | stickCorner xy m = M.insert xy LightOn m 73 | 74 | -- | Strategy: Run the given transformation forward to the end 75 | -- and print the resulting light count. 76 | soln :: (LightMap -> LightMap) -> String -> IO () 77 | soln nextFrame stuff = do 78 | print $ countLights indices $ makeFrames !! nFrames 79 | where 80 | makeFrames = iterate nextFrame $ readLights stuff 81 | 82 | solna :: String -> IO () 83 | solna stuff = do 84 | soln playLife stuff 85 | 86 | solnb :: String -> IO () 87 | solnb stuff = do 88 | soln (stickCorners . playLife) stuff 89 | 90 | main :: IO () 91 | main = makeMain solna solnb 92 | -------------------------------------------------------------------------------- /19/Makefile: -------------------------------------------------------------------------------- 1 | ../Soln/Makefile.template -------------------------------------------------------------------------------- /19/README.md: -------------------------------------------------------------------------------- 1 | Ah, term rewriting. Part A was relatively straightforward, 2 | and the solution is reasonable Haskell. 3 | 4 | Part B was a nightmare. I don't even want to say all the 5 | things I tried, all of which were either too complex to 6 | code, or too inefficient to run. I finally super-cheezed it 7 | using a 8 | [hack](https://www.reddit.com/r/adventofcode/comments/3xflz8/day_19_solutions/cy4h7ji) 9 | I found on Reddit way after the fact. Once I saw it, I 10 | immediately understood why it worked, so whatever. 11 | 12 | Needless to say, I'm not too happy with the problem of Part 13 | B. It is not efficiently decidable in general, and even in 14 | this special case it's a pain to do properly. Not really 15 | in the spirit of the other problems. 16 | 17 | The final Haskell is unremarkable. I have a separate repo 18 | lying around with many terrible branches, but I don't feel 19 | like showing them to anyone. 20 | -------------------------------------------------------------------------------- /19/input.txt: -------------------------------------------------------------------------------- 1 | Al => ThF 2 | Al => ThRnFAr 3 | B => BCa 4 | B => TiB 5 | B => TiRnFAr 6 | Ca => CaCa 7 | Ca => PB 8 | Ca => PRnFAr 9 | Ca => SiRnFYFAr 10 | Ca => SiRnMgAr 11 | Ca => SiTh 12 | F => CaF 13 | F => PMg 14 | F => SiAl 15 | H => CRnAlAr 16 | H => CRnFYFYFAr 17 | H => CRnFYMgAr 18 | H => CRnMgYFAr 19 | H => HCa 20 | H => NRnFYFAr 21 | H => NRnMgAr 22 | H => NTh 23 | H => OB 24 | H => ORnFAr 25 | Mg => BF 26 | Mg => TiMg 27 | N => CRnFAr 28 | N => HSi 29 | O => CRnFYFAr 30 | O => CRnMgAr 31 | O => HP 32 | O => NRnFAr 33 | O => OTi 34 | P => CaP 35 | P => PTi 36 | P => SiRnFAr 37 | Si => CaSi 38 | Th => ThCa 39 | Ti => BP 40 | Ti => TiTi 41 | e => HF 42 | e => NAl 43 | e => OMg 44 | 45 | CRnCaSiRnBSiRnFArTiBPTiTiBFArPBCaSiThSiRnTiBPBPMgArCaSiRnTiMgArCaSiThCaSiRnFArRnSiRnFArTiTiBFArCaCaSiRnSiThCaCaSiRnMgArFYSiRnFYCaFArSiThCaSiThPBPTiMgArCaPRnSiAlArPBCaCaSiRnFYSiThCaRnFArArCaCaSiRnPBSiRnFArMgYCaCaCaCaSiThCaCaSiAlArCaCaSiRnPBSiAlArBCaCaCaCaSiThCaPBSiThPBPBCaSiRnFYFArSiThCaSiRnFArBCaCaSiRnFYFArSiThCaPBSiThCaSiRnPMgArRnFArPTiBCaPRnFArCaCaCaCaSiRnCaCaSiRnFYFArFArBCaSiThFArThSiThSiRnTiRnPMgArFArCaSiThCaPBCaSiRnBFArCaCaPRnCaCaPMgArSiRnFYFArCaSiThRnPBPMgAr 46 | -------------------------------------------------------------------------------- /19/problem-a.md: -------------------------------------------------------------------------------- 1 | Rudolph the Red-Nosed Reindeer is sick! His nose isn't 2 | shining very brightly, and he needs medicine. 3 | 4 | Red-Nosed Reindeer biology isn't similar to regular reindeer 5 | biology; Rudolph is going to need custom-made 6 | medicine. Unfortunately, Red-Nosed Reindeer chemistry isn't 7 | similar to regular reindeer chemistry, either. 8 | 9 | The North Pole is equipped with a Red-Nosed Reindeer nuclear 10 | fusion/fission plant, capable of constructing any Red-Nosed 11 | Reindeer molecule you need. It works by starting with some 12 | input molecule and then doing a series of replacements, one 13 | per step, until it has the right molecule. 14 | 15 | However, the machine has to be calibrated before it can be 16 | used. Calibration involves determining the number of 17 | molecules that can be generated in one step from a given 18 | starting point. 19 | 20 | For example, imagine a simpler machine that supports only 21 | the following replacements: 22 | 23 | H => HO 24 | H => OH 25 | O => HH 26 | 27 | Given the replacements above and starting with HOH, the following molecules could be generated: 28 | 29 | HOOH (via H => HO on the first H). 30 | HOHO (via H => HO on the second H). 31 | OHOH (via H => OH on the first H). 32 | HOOH (via H => OH on the second H). 33 | HHHH (via O => HH). 34 | 35 | So, in the example above, there are 4 distinct molecules 36 | (not five, because HOOH appears twice) after one replacement 37 | from HOH. Santa's favorite molecule, HOHOHO, can become 7 38 | distinct molecules (over nine replacements: six from H, and 39 | three from O). 40 | 41 | The machine replaces without regard for the surrounding 42 | characters. For example, given the string H2O, the 43 | transition H => OO would result in OO2O. 44 | 45 | Your puzzle input describes all of the possible replacements 46 | and, at the bottom, the medicine molecule for which you need 47 | to calibrate the machine. How many distinct molecules can be 48 | created after all the different ways you can do one 49 | replacement on the medicine molecule? 50 | 51 | -------------------------------------------------------------------------------- /19/problem-b.md: -------------------------------------------------------------------------------- 1 | Now that the machine is calibrated, you're ready to begin 2 | molecule fabrication. 3 | 4 | Molecule fabrication always begins with just a single 5 | electron, e, and applying replacements one at a time, just 6 | like the ones during calibration. 7 | 8 | For example, suppose you have the following replacements: 9 | 10 | e => H 11 | e => O 12 | H => HO 13 | H => OH 14 | O => HH 15 | 16 | If you'd like to make HOH, you start with e, and then make 17 | the following replacements: 18 | 19 | e => O to get O 20 | O => HH to get HH 21 | H => OH (on the second H) to get HOH 22 | 23 | So, you could make HOH after 3 steps. Santa's favorite 24 | molecule, HOHOHO, can be made in 6 steps. 25 | 26 | How long will it take to make the medicine? Given the 27 | available replacements and the medicine molecule in your 28 | puzzle input, what is the fewest number of steps to go from 29 | e to the medicine molecule? 30 | -------------------------------------------------------------------------------- /19/soln.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | 7 | import qualified Data.Map.Strict as M 8 | import qualified Data.Set as S 9 | 10 | import Soln 11 | 12 | -- | Map from molecule (don't ask) to list of molecules. 13 | type TransMap = M.Map [String] [[String]] 14 | 15 | -- | Insert a new molecule in the list of productions for 16 | -- a given molecule. 17 | insertMulti :: [String] -> [String] -> TransMap -> TransMap 18 | insertMulti k v m = 19 | M.insertWith (\[new] old -> new : old) k [v] m 20 | 21 | -- | Read a molecule. 22 | parseElem :: [String] -> String -> ([String], String) 23 | parseElem cs (s1 : s2 : ss) | isUpper s1 && isLower s2 = 24 | (cs ++ [[s1, s2]], ss) 25 | parseElem cs (s : ss) | isUpper s = 26 | (cs ++ [[s]], ss) 27 | parseElem _ _ = error "bad elem" 28 | 29 | -- | Read a rule. 30 | parseRule :: [String] -> TransMap -> TransMap 31 | parseRule [src, "=>", dst] m = 32 | let es = envelop parseElem [] dst in 33 | insertMulti [src] es m 34 | parseRule _ _ = error "bad rule" 35 | 36 | -- | Read a machine specification, both rules and input. 37 | parseMachine :: String -> (TransMap, [String]) 38 | parseMachine stuff = 39 | let desc = lines stuff 40 | rules = take (length desc - 2) desc 41 | m = foldr (parseRule . words) M.empty rules 42 | elems = envelop parseElem [] $ last desc 43 | in 44 | (m, elems) 45 | 46 | -- | Expansions of a given molecule starting at a given 47 | -- split point. 48 | expandSplit :: TransMap -> ([String], [String]) -> S.Set [String] 49 | -> S.Set [String] 50 | expandSplit _ (_, []) _ = error "bad expansion" 51 | expandSplit m (first, rest) result = 52 | M.foldrWithKey' expandOne result m 53 | where 54 | expandOne :: [String] -> [[String]] -> S.Set [String] -> S.Set [String] 55 | expandOne k vs a 56 | | isPrefixOf k rest = 57 | foldr (S.insert . splice) a vs 58 | | otherwise = a 59 | where 60 | splice :: [String] -> [String] 61 | splice v = first ++ v ++ drop (length k) rest 62 | 63 | -- | Expansions of a given set of molecules. 64 | expand :: TransMap -> S.Set [String] -> S.Set [String] 65 | expand m s0 = 66 | S.foldr' oneItem S.empty s0 67 | where 68 | oneItem es s = 69 | foldr (expandSplit m) (s `S.difference` s0) $ 70 | map (flip splitAt es) $ [0 .. length es - 1] 71 | 72 | solna :: String -> IO () 73 | solna stuff = do 74 | let (m, es) = parseMachine stuff 75 | print $ S.size $ expand m $ S.singleton es 76 | 77 | -- | Solution from 78 | -- https://www.reddit.com/r/adventofcode/ 79 | -- comments/3xflz8/day_19_solutions/cy4h7ji 80 | solnb :: String -> IO () 81 | solnb stuff = do 82 | let (_, es) = parseMachine stuff 83 | let n = length es 84 | let c s = length $ filter (== s) es 85 | print $ n - c "Rn" - c "Ar" - 2 * c "Y" - 1 86 | 87 | main :: IO () 88 | main = makeMain solna solnb 89 | -------------------------------------------------------------------------------- /19/test.txt: -------------------------------------------------------------------------------- 1 | H => HO 2 | H => OH 3 | O => HH 4 | e => H 5 | e => O 6 | 7 | HOHOHO 8 | -------------------------------------------------------------------------------- /2/Makefile: -------------------------------------------------------------------------------- 1 | ../Soln/Makefile.template -------------------------------------------------------------------------------- /2/README.md: -------------------------------------------------------------------------------- 1 | This is mostly an exercise in careful reading and 2 | implementing of the specification. 3 | 4 | The Haskell feature used here is first-class functions, 5 | which allow us to use the same solution for both 6 | box-wrapping problems, and for any other future problems 7 | where the total wrapping cost is the sum of the cost of 8 | wrapping individual boxes. 9 | -------------------------------------------------------------------------------- /2/go-soln.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "bufio" 7 | "os" 8 | "log" 9 | "strings" 10 | "strconv" 11 | ) 12 | 13 | func openFile(name string) *os.File { 14 | f, err := os.Open(name) 15 | 16 | if err != nil { 17 | log.Fatalf("Cannot open input file.") 18 | } 19 | 20 | return f 21 | } 22 | 23 | func parseDimension(s string) (l int, w int, h int) { 24 | p := strings.Split(strings.TrimSpace(s), "x") 25 | 26 | l, _ = strconv.Atoi(p[0]) 27 | w, _ = strconv.Atoi(p[1]) 28 | h, _ = strconv.Atoi(p[2]) 29 | 30 | return 31 | } 32 | 33 | func calculateTotal(l, w, h int) int { 34 | s1, s2, s3 := (l * w), (w * h), (h * l) 35 | 36 | s := findSlack(s1, s2, s3) 37 | 38 | return (2 * s1) + (2 * s2) + (2 * s3) + (s) 39 | } 40 | 41 | func findSlack(nums ...int) int { 42 | if len(nums) == 1 { 43 | return nums[0] 44 | } 45 | 46 | s := nums[0] 47 | 48 | for _, n := range nums[1:] { 49 | if n < s { 50 | s = n 51 | } 52 | } 53 | 54 | return s 55 | } 56 | 57 | func main() { 58 | file := openFile("./input.txt") 59 | defer file.Close() 60 | 61 | reader := bufio.NewReader(file) 62 | 63 | total := 0 64 | 65 | for { 66 | line, err := reader.ReadString('\n') 67 | 68 | if line != "" { 69 | l, w, h := parseDimension(line) 70 | 71 | total += calculateTotal(l, w, h) 72 | 73 | if err == io.EOF { 74 | break 75 | } 76 | } 77 | 78 | fmt.Printf("Elves should order %d square feet of wrapping paper.\n", total) 79 | } 80 | -------------------------------------------------------------------------------- /2/problem-a.md: -------------------------------------------------------------------------------- 1 | The elves are running low on wrapping paper, and so they 2 | need to submit an order for more. They have a list of the 3 | dimensions (length l, width w, and height h) of each 4 | present, and only want to order exactly as much as they 5 | need. 6 | 7 | Fortunately, every present is a box (a perfect right 8 | rectangular prism), which makes calculating the required 9 | wrapping paper for each gift a little easier: find the 10 | surface area of the box, which is *2\*l\*w + 2\*w\*h + 11 | 2\*h\*l.* The elves also need a little extra paper for each 12 | present: the area of the smallest side. 13 | 14 | For example: 15 | 16 | * A present with dimensions 2x3x4 requires 2\*6 + 2\*12 + 17 | 2\*8 = 52 square feet of wrapping paper plus 6 square feet 18 | of slack, for a total of 58 square feet. 19 | 20 | * A present with dimensions 1x1x10 requires 2\*1 + 2\*10 + 21 | 2\*10 = 42 square feet of wrapping paper plus 1 square foot 22 | of slack, for a total of 43 square feet. 23 | 24 | All numbers in the elves' list are in feet. How many total 25 | square feet of wrapping paper should they order? 26 | -------------------------------------------------------------------------------- /2/problem-b.md: -------------------------------------------------------------------------------- 1 | The elves are also running low on ribbon. Ribbon is all the 2 | same width, so they only have to worry about the length they 3 | need to order, which they would again like to be exact. 4 | 5 | The ribbon required to wrap a present is the shortest 6 | distance around its sides, or the smallest perimeter of any 7 | one face. Each present also requires a bow made out of 8 | ribbon as well; the feet of ribbon required for the perfect 9 | bow is equal to the cubic feet of volume of the 10 | present. Don't ask how they tie the bow, though; they'll 11 | never tell. 12 | 13 | For example: 14 | 15 | * A present with dimensions 2x3x4 requires 2+2+3+3 = 10 feet 16 | of ribbon to wrap the present plus 2*3*4 = 24 feet of 17 | ribbon for the bow, for a total of 34 feet. 18 | 19 | * A present with dimensions 1x1x10 requires 1+1+1+1 = 4 feet 20 | of ribbon to wrap the present plus 1*1*10 = 10 feet of 21 | ribbon for the bow, for a total of 14 feet. 22 | 23 | How many total feet of ribbon should they order? 24 | -------------------------------------------------------------------------------- /2/soln.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | 7 | import Soln 8 | 9 | -- | Returns the minimum two of three sides. The 10 | -- subscripts are awkward here, but at least we 11 | -- have the tuple lengths as part of the type for 12 | -- static checking. 13 | minSides :: (Int, Int, Int) -> (Int, Int) 14 | minSides (n1, n2, n3) = 15 | let [m1, m2] = take 2 $ sort [n1, n2, n3] in 16 | (m1, m2) 17 | 18 | -- | Convert a tuple description into a tuple. 19 | parseTuple :: String -> (Int, Int, Int) 20 | parseTuple s = 21 | case words $ map fixx s of 22 | [n1, n2, n3] -> (read n1, read n2, read n3) 23 | _ -> error "bad tuple" 24 | where 25 | fixx 'x' = ' ' 26 | fixx c | isDigit c = c 27 | fixx _ = error "bad character in input" 28 | 29 | -- | The cost of a minimum-area wrapping. 30 | area :: (Int, Int, Int) -> Int 31 | area sides@(n1, n2, n3) = 32 | let (m1, m2) = minSides sides in 33 | m1 * m2 + 2 * (n1 * n2 + n2 * n3 + n1 * n3) 34 | 35 | -- | The volume of a box. 36 | volume :: (Int, Int, Int) -> Int 37 | volume (n1, n2, n3) = n1 * n2 * n3 38 | 39 | -- | The ribbon footage of a box. 40 | footage :: (Int, Int, Int) -> Int 41 | footage sides = 42 | let (m1, m2) = minSides sides in 43 | 2 * (m1 + m2) + volume sides 44 | 45 | -- | Given a cost function for an individual box, 46 | -- print the total cost of wrapping all boxes in the input. 47 | soln :: ((Int, Int, Int) -> Int) -> String -> IO () 48 | soln fCost stuff = 49 | print $ sum $ map (fCost . parseTuple) $ lines stuff 50 | 51 | solna :: String -> IO () 52 | solna = soln area 53 | 54 | solnb :: String -> IO () 55 | solnb = soln footage 56 | 57 | main :: IO () 58 | main = makeMain solna solnb 59 | -------------------------------------------------------------------------------- /20/Factor.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | 7 | module Factor 8 | where 9 | 10 | import Data.List 11 | 12 | -- | Integer square root. 13 | -- 14 | isqrt :: Int -> Int 15 | isqrt 0 = 0 16 | isqrt 1 = 1 17 | isqrt n = 18 | let twopows = iterate square 2 19 | (lowerRoot, lowerN) = 20 | last $ takeWhile ((n>=) . snd) $ zip (1:twopows) twopows 21 | newtonStep x = div (x + div n x) 2 22 | iters = iterate newtonStep (isqrt (div n lowerN) * lowerRoot) 23 | isRoot r = square r <= n && n < square (r+1) 24 | in head $ dropWhile (not . isRoot) iters 25 | where 26 | square x = x * x 27 | 28 | -- | List of primes by trial division. 29 | primes :: [Int] 30 | primes = 2 : [ i | i <- [3..], 31 | and [i `mod` j /= 0 | 32 | j <- takeWhile (<= isqrt i) primes]] 33 | 34 | -- | Prime factors. 35 | factors :: Int -> [Int] 36 | factors n | n < 2 = error "cannot factor" 37 | factors n0 = f n0 primes where 38 | f _ [] = error "no factors" 39 | f n (c : cs) 40 | | n == c = [c] 41 | | n `mod` c == 0 = c : f (n `div` c) (c : cs) 42 | | otherwise = f n cs 43 | 44 | -- | All factors including composites. 45 | allFactors :: Int -> [Int] 46 | allFactors n = 47 | processMultiples $ group $ factors n 48 | where 49 | multiples :: [Int] -> [Int] 50 | multiples g = map product $ inits g 51 | processMultiples :: [[Int]] -> [Int] 52 | processMultiples [] = [] 53 | processMultiples [g] = 54 | multiples g 55 | processMultiples (g : gs) = 56 | let ms = multiples g 57 | fs = processMultiples gs in 58 | [ m * f | f <- fs, m <- ms ] 59 | -------------------------------------------------------------------------------- /20/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright © 2015 Bart Massey 2 | # [This program is licensed under the "MIT License"] 3 | # Please see the file COPYING in the source 4 | # distribution of this software for license terms. 5 | 6 | HC = ghc 7 | HCFLAGS = -i../Soln -Wall -O2 8 | 9 | soln: soln.hs Factor.hs 10 | $(HC) $(HCFLAGS) --make soln.hs 11 | 12 | clean: 13 | -rm -f soln.hi soln.o soln Factor.o Factor.hi 14 | -------------------------------------------------------------------------------- /20/README.md: -------------------------------------------------------------------------------- 1 | Hey, look! A [Project Euler](http://projecteuler.net/) 2 | problem snuck in here. 3 | 4 | The brute-force solution is ugly, but instead notice that a 5 | house will get presents equal to 10 (resp. 11) the sum of 6 | all the factors (including composite factors) of its house 7 | number. 8 | 9 | That said, this Haskell solution is still really slow: about 10 | a minute of runtime. There's probably something much faster 11 | given enough careful number-theoretic thinking, but I'll 12 | just be dumb. 13 | 14 | The module `Factor.hs` was borrowed from my Project 15 | Euler solutions and upgraded a bit for clarity and 16 | correctness. Turns out I'm a better Haskell programmer 17 | in 2015 than in 2008: who knew? 18 | -------------------------------------------------------------------------------- /20/input.txt: -------------------------------------------------------------------------------- 1 | 36000000 2 | -------------------------------------------------------------------------------- /20/problem-a.md: -------------------------------------------------------------------------------- 1 | To keep the Elves busy, Santa has them deliver some presents 2 | by hand, door-to-door. He sends them down a street with 3 | infinite houses numbered sequentially\: 1, 2, 3, 4, 5, and so 4 | on. 5 | 6 | Each Elf is assigned a number, too, and delivers presents to 7 | houses based on that number: 8 | 9 | * The first Elf (number 1) delivers presents to every house: 10 | 1, 2, 3, 4, 5, .... 11 | * The second Elf (number 2) delivers presents to every 12 | second house: 2, 4, 6, 8, 10, .... 13 | * Elf number 3 delivers presents to every third house: 3, 6, 14 | 9, 12, 15, .... 15 | 16 | There are infinitely many Elves, numbered starting 17 | with 1. Each Elf delivers presents equal to ten times his or 18 | her number at each house. 19 | 20 | So, the first nine houses on the street end up like this: 21 | 22 | House 1 got 10 presents. 23 | House 2 got 30 presents. 24 | House 3 got 40 presents. 25 | House 4 got 70 presents. 26 | House 5 got 60 presents. 27 | House 6 got 120 presents. 28 | House 7 got 80 presents. 29 | House 8 got 150 presents. 30 | House 9 got 130 presents. 31 | 32 | The first house gets 10 presents: it is visited only by Elf 33 | 1, which delivers 1 * 10 = 10 presents. The fourth house 34 | gets 70 presents, because it is visited by Elves 1, 2, and 35 | 4, for a total of 10 + 20 + 40 = 70 presents. 36 | 37 | What is the lowest house number of the house to get at least 38 | as many presents as the number in your puzzle input? 39 | -------------------------------------------------------------------------------- /20/problem-b.md: -------------------------------------------------------------------------------- 1 | The Elves decide they don't want to visit an infinite number 2 | of houses. Instead, each Elf will stop after delivering 3 | presents to 50 houses. To make up for it, they decide to 4 | deliver presents equal to eleven times their number at each 5 | house. 6 | 7 | With these changes, what is the new lowest house number of 8 | the house to get at least as many presents as the number in 9 | your puzzle input? 10 | 11 | -------------------------------------------------------------------------------- /20/soln.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | 7 | import Factor 8 | import Soln 9 | 10 | -- | Number of presents at house 'i' in part A. 11 | nPresents :: Int -> Int 12 | nPresents i = 13 | 10 * sum (allFactors i) 14 | 15 | -- | Strategy: Keep checking houses. 16 | solna :: String -> IO () 17 | solna stuff = do 18 | let t = read stuff 19 | let enoughPresents n = t <= nPresents n 20 | print $ fromJust $ find enoughPresents $ [1..] 21 | 22 | -- | Number of presents at house 'i' in part B. 23 | nPresents' :: Int -> Int 24 | nPresents' i = 25 | 11 * (sum $ filter stillElfing $ allFactors i) 26 | where 27 | stillElfing f = 28 | i `div` f <= 50 29 | 30 | -- | Strategy: Keep checking houses. 31 | solnb :: String -> IO () 32 | solnb stuff = do 33 | let t = read stuff 34 | let enoughPresents n = t <= nPresents' n 35 | print $ fromJust $ find enoughPresents $ [1..] 36 | 37 | main :: IO () 38 | main = makeMain solna solnb 39 | -------------------------------------------------------------------------------- /21/Makefile: -------------------------------------------------------------------------------- 1 | ../Soln/Makefile.template -------------------------------------------------------------------------------- /21/README.md: -------------------------------------------------------------------------------- 1 | Cool. A little RPG simulator. Probably easiest to just 2 | brute force it as usual. 3 | 4 | This Haskell solution makes good use of `do` notation in the 5 | list monad to generate all possible equipped PCs. The item 6 | table should probably be read from a file instead of being 7 | hard-coded. 8 | -------------------------------------------------------------------------------- /21/input.txt: -------------------------------------------------------------------------------- 1 | Hit Points: 100 2 | Damage: 8 3 | Armor: 2 4 | -------------------------------------------------------------------------------- /21/problem-a.md: -------------------------------------------------------------------------------- 1 | Little Henry Case got a new video game for Christmas. It's 2 | an RPG, and he's stuck on a boss. He needs to know what 3 | equipment to buy at the shop. He hands you the controller. 4 | 5 | In this game, the player (you) and the enemy (the boss) take 6 | turns attacking. The player always goes first. Each attack 7 | reduces the opponent's hit points by at least 1. The first 8 | character at or below 0 hit points loses. 9 | 10 | Damage dealt by an attacker each turn is equal to the 11 | attacker's damage score minus the defender's armor score. An 12 | attacker always does at least 1 damage. So, if the attacker 13 | has a damage score of 8, and the defender has an armor score 14 | of 3, the defender loses 5 hit points. If the defender had 15 | an armor score of 300, the defender would still lose 1 hit 16 | point. 17 | 18 | Your damage score and armor score both start at zero. They 19 | can be increased by buying items in exchange for gold. You 20 | start with no items and have as much gold as you need. Your 21 | total damage or armor is equal to the sum of those stats 22 | from all of your items. You have 100 hit points. 23 | 24 | Here is what the item shop is selling: 25 | 26 | Weapons: Cost Damage Armor 27 | Dagger 8 4 0 28 | Shortsword 10 5 0 29 | Warhammer 25 6 0 30 | Longsword 40 7 0 31 | Greataxe 74 8 0 32 | 33 | Armor: Cost Damage Armor 34 | Leather 13 0 1 35 | Chainmail 31 0 2 36 | Splintmail 53 0 3 37 | Bandedmail 75 0 4 38 | Platemail 102 0 5 39 | 40 | Rings: Cost Damage Armor 41 | Damage +1 25 1 0 42 | Damage +2 50 2 0 43 | Damage +3 100 3 0 44 | Defense +1 20 0 1 45 | Defense +2 40 0 2 46 | Defense +3 80 0 3 47 | 48 | You must buy exactly one weapon; no dual-wielding. Armor is 49 | optional, but you c an't use more than one. You can buy 0-2 50 | rings (at most one for each hand). You must use any items 51 | you buy. The shop only has one of each item, so you can't 52 | buy, for example, two rings of Damage +3. 53 | 54 | For example, suppose you have 8 hit points, 5 damage, and 5 55 | armor, and that the boss has 12 hit points, 7 damage, and 2 56 | armor: 57 | 58 | The player deals 5-2 = 3 damage; the boss goes down to 9 hit points. 59 | The boss deals 7-5 = 2 damage; the player goes down to 6 hit points. 60 | The player deals 5-2 = 3 damage; the boss goes down to 6 hit points. 61 | The boss deals 7-5 = 2 damage; the player goes down to 4 hit points. 62 | The player deals 5-2 = 3 damage; the boss goes down to 3 hit points. 63 | The boss deals 7-5 = 2 damage; the player goes down to 2 hit points. 64 | The player deals 5-2 = 3 damage; the boss goes down to 0 hit points. 65 | 66 | In this scenario, the player wins! (Barely.) 67 | 68 | You have 100 hit points. The boss's actual stats are in your 69 | puzzle input. What is the least amount of gold you can spend 70 | and still win the fight? 71 | 72 | -------------------------------------------------------------------------------- /21/problem-b.md: -------------------------------------------------------------------------------- 1 | Turns out the shopkeeper is working with the boss, and can 2 | persuade you to buy whatever items he wants. The other rules 3 | still apply, and he still only has one of each item. 4 | 5 | What is the most amount of gold you can spend and still lose 6 | the fight? 7 | 8 | -------------------------------------------------------------------------------- /21/soln.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | 7 | import Soln 8 | 9 | -- | The various item types. 10 | data ItemType = Weapon | Armor | Ring 11 | deriving (Eq, Show) 12 | 13 | -- | Description of an item. 14 | data Item = Item { 15 | itemType :: ItemType, 16 | itemName :: String, 17 | itemCost :: Int, 18 | itemDamage :: Int, 19 | itemArmor :: Int } 20 | deriving Show 21 | 22 | -- | Us vs them. 23 | data XPC = PC | NPC deriving (Eq, Show) 24 | 25 | -- | Description of an XPC. 26 | data Stats = Stats { 27 | statsType :: XPC, 28 | statsHP :: Int, 29 | statsDamage :: Int, 30 | statsArmor :: Int, 31 | statsCost :: Int, 32 | statsGear :: [String] } 33 | deriving Show 34 | 35 | -- | Parse the NPC description. 36 | parseStats :: String -> Stats 37 | parseStats stuff = 38 | case map words $ lines stuff of 39 | [[ "Hit", "Points:", hpstr ], 40 | [ "Damage:", dstr ], 41 | [ "Armor:", astr ]] -> 42 | Stats NPC (read hpstr) (read dstr) (read astr) 0 [] 43 | _ -> error "bad stats" 44 | 45 | 46 | -- | Run the fight. 47 | fightSim :: Stats -> Stats -> XPC 48 | fightSim npc pc = 49 | case find dead $ iterate attack (pc, npc) of 50 | Just (_, defender) -> statsType defender 51 | _ -> error "fight finished inconclusively" 52 | where 53 | dead (attacker, _) = statsHP attacker <= 0 54 | attack (attacker, defender) = 55 | (defender {statsHP = calcDamage}, attacker) 56 | where 57 | calcDamage = (statsHP defender) - 58 | (1 `max` (statsDamage attacker - statsArmor defender)) 59 | 60 | -- | The item table. 61 | items :: [Item] 62 | items = [ 63 | -- Weapons: Cost Damage Armor 64 | Item Weapon "Dagger" 8 4 0, 65 | Item Weapon "Shortsword" 10 5 0, 66 | Item Weapon "Warhammer" 25 6 0, 67 | Item Weapon "Longsword" 40 7 0, 68 | Item Weapon "Greataxe" 74 8 0, 69 | 70 | -- Armor: Cost Damage Armor 71 | Item Armor "Leather" 13 0 1, 72 | Item Armor "Chainmail" 31 0 2, 73 | Item Armor "Splintmail" 53 0 3, 74 | Item Armor "Bandedmail" 75 0 4, 75 | Item Armor "Platemail" 102 0 5, 76 | 77 | -- Rings: Cost Damage Armor 78 | Item Ring "Damage +1" 25 1 0, 79 | Item Ring "Damage +2" 50 2 0, 80 | Item Ring "Damage +3" 100 3 0, 81 | Item Ring "Defense +1" 20 0 1, 82 | Item Ring "Defense +2" 40 0 2, 83 | Item Ring "Defense +3" 80 0 3 ] 84 | 85 | -- | List of all possible equipped PCs. 86 | equippedPCs :: [Stats] 87 | equippedPCs = 88 | map outfit gearChoices 89 | where 90 | getItems targetType = 91 | filter ((== targetType) . itemType) items 92 | ringCombos = 93 | map (:[]) rings ++ ringPairs 94 | where 95 | rings = getItems Ring 96 | ringPairs = do 97 | (r1 : rs) <- tails rings 98 | r2 <- rs 99 | return [r1, r2] 100 | gearChoices = do 101 | weapon <- getItems Weapon 102 | armor <- [] : map (:[]) (getItems Armor) 103 | rings <- [] : ringCombos 104 | return $ weapon : (armor ++ rings) 105 | outfit gear = 106 | foldr applyGear nakedPlayer gear 107 | where 108 | nakedPlayer = Stats PC 100 0 0 0 [] 109 | applyGear item player = 110 | player { 111 | statsDamage = statsDamage player + itemDamage item, 112 | statsArmor = statsArmor player + itemArmor item, 113 | statsCost = statsCost player + itemCost item, 114 | statsGear = itemName item : statsGear player } 115 | 116 | -- | Strategy: Brute force. 117 | soln :: (Stats -> Int) -> XPC -> String -> IO () 118 | soln sf target stuff = do 119 | let boss = parseStats stuff 120 | case find ((target ==) . fightSim boss) pcs of 121 | Nothing -> error "no cromulent equipment" 122 | Just pc -> print $ statsCost pc 123 | where 124 | pcs = sortBy (comparing sf) equippedPCs 125 | 126 | solna :: String -> IO () 127 | solna stuff = 128 | soln statsCost PC stuff 129 | 130 | solnb :: String -> IO () 131 | solnb stuff = do 132 | soln (negate . statsCost) NPC stuff 133 | 134 | main :: IO () 135 | main = makeMain solna solnb 136 | -------------------------------------------------------------------------------- /22/.gitignore: -------------------------------------------------------------------------------- 1 | /old-soln 2 | -------------------------------------------------------------------------------- /22/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright © 2015 Bart Massey 2 | # [This program is licensed under the "MIT License"] 3 | # Please see the file COPYING in the source 4 | # distribution of this software for license terms. 5 | 6 | HC = ghc 7 | HCFLAGS = -i../Soln -Wall -O2 8 | 9 | soln: soln.hs 10 | $(HC) $(HCFLAGS) --make soln.hs 11 | 12 | old-soln: old-soln.hs 13 | $(HC) $(HCFLAGS) --make old-soln.hs 14 | 15 | clean: 16 | -rm -f soln.hi soln.o soln 17 | -rm -f old-soln.hi old-soln.o old-soln 18 | ( cd ../Soln && make clean ) 19 | -------------------------------------------------------------------------------- /22/README.md: -------------------------------------------------------------------------------- 1 | This problem *looks* as easy as Problem 21. While it does 2 | indeed reuse some code from 21, it's a much trickier problem 3 | in practice. 4 | 5 | The sequencing is crucial, and I am not even sure that the 6 | sequencing in the accepted solution is what is described by 7 | the fairly loose problem description. What I mean by this is 8 | that spell effects and effect expiration must be applied in 9 | exactly the right order at exactly the right time, or the 10 | whole thing gets wrong answers. 11 | 12 | There are two solutions here. `old-soln.hs` is what I 13 | originally wrote to solve the problem. `soln.hs` is 14 | the properly engineered version: it uses the `Either` monad 15 | and better decomposition to remove the recursion and to 16 | make the code actually readable and understandable; it 17 | also removes a fiddly fudge factor that shouldn't have been 18 | there and changes the runtime for Part A from many seconds 19 | to instantaneous. 20 | 21 | I spent many, many hours debugging both solutions. There is 22 | some testing code here that was crucial to getting things 23 | right. 24 | -------------------------------------------------------------------------------- /22/input.txt: -------------------------------------------------------------------------------- 1 | Hit Points: 58 2 | Damage: 9 3 | -------------------------------------------------------------------------------- /22/problem-a.md: -------------------------------------------------------------------------------- 1 | Little Henry Case decides that defeating bosses with swords and stuff is boring. Now he's playing the game with a wizard. Of course, he gets stuck on another boss and needs your help again. 2 | 3 | In this version, combat still proceeds with the player and the boss taking alternating turns. The player still goes first. Now, however, you don't get any equipment; instead, you must choose one of your spells to cast. The first character at or below 0 hit points loses. 4 | 5 | Since you're a wizard, you don't get to wear armor, and you can't attack normally. However, since you do magic damage, your opponent's armor is ignored, and so the boss effectively has zero armor as well. As before, if armor (from a spell, in this case) would reduce damage below 1, it becomes 1 instead - that is, the boss' attacks always deal at least 1 damage. 6 | 7 | On each of your turns, you must select one of your spells to cast. If you cannot afford to cast any spell, you lose. Spells cost mana; you start with 500 mana, but have no maximum limit. You must have enough mana to cast a spell, and its cost is immediately deducted when you cast it. Your spells are Magic Missile, Drain, Shield, Poison, and Recharge. 8 | 9 | Magic Missile costs 53 mana. It instantly does 4 damage. 10 | Drain costs 73 mana. It instantly does 2 damage and heals you for 2 hit points. 11 | Shield costs 113 mana. It starts an effect that lasts for 6 turns. While it is active, your armor is increased by 7. 12 | Poison costs 173 mana. It starts an effect that lasts for 6 turns. At the start of each turn while it is active, it deals the boss 3 damage. 13 | Recharge costs 229 mana. It starts an effect that lasts for 5 turns. At the start of each turn while it is active, it gives you 101 new mana. 14 | Effects all work the same way. Effects apply at the start of both the player's turns and the boss' turns. Effects are created with a timer (the number of turns they last); at the start of each turn, after they apply any effect they have, their timer is decreased by one. If this decreases the timer to zero, the effect ends. You cannot cast a spell that would start an effect which is already active. However, effects can be started on the same turn they end. 15 | 16 | For example, suppose the player has 10 hit points and 250 mana, and that the boss has 13 hit points and 8 damage: 17 | 18 | -- Player turn -- 19 | - Player has 10 hit points, 0 armor, 250 mana 20 | - Boss has 13 hit points 21 | Player casts Poison. 22 | 23 | -- Boss turn -- 24 | - Player has 10 hit points, 0 armor, 77 mana 25 | - Boss has 13 hit points 26 | Poison deals 3 damage; its timer is now 5. 27 | Boss attacks for 8 damage. 28 | 29 | -- Player turn -- 30 | - Player has 2 hit points, 0 armor, 77 mana 31 | - Boss has 10 hit points 32 | Poison deals 3 damage; its timer is now 4. 33 | Player casts Magic Missile, dealing 4 damage. 34 | 35 | -- Boss turn -- 36 | - Player has 2 hit points, 0 armor, 24 mana 37 | - Boss has 3 hit points 38 | Poison deals 3 damage. This kills the boss, and the player wins. 39 | Now, suppose the same initial conditions, except that the boss has 14 hit points instead: 40 | 41 | -- Player turn -- 42 | - Player has 10 hit points, 0 armor, 250 mana 43 | - Boss has 14 hit points 44 | Player casts Recharge. 45 | 46 | -- Boss turn -- 47 | - Player has 10 hit points, 0 armor, 21 mana 48 | - Boss has 14 hit points 49 | Recharge provides 101 mana; its timer is now 4. 50 | Boss attacks for 8 damage! 51 | 52 | -- Player turn -- 53 | - Player has 2 hit points, 0 armor, 122 mana 54 | - Boss has 14 hit points 55 | Recharge provides 101 mana; its timer is now 3. 56 | Player casts Shield, increasing armor by 7. 57 | 58 | -- Boss turn -- 59 | - Player has 2 hit points, 7 armor, 110 mana 60 | - Boss has 14 hit points 61 | Shield's timer is now 5. 62 | Recharge provides 101 mana; its timer is now 2. 63 | Boss attacks for 8 - 7 = 1 damage! 64 | 65 | -- Player turn -- 66 | - Player has 1 hit point, 7 armor, 211 mana 67 | - Boss has 14 hit points 68 | Shield's timer is now 4. 69 | Recharge provides 101 mana; its timer is now 1. 70 | Player casts Drain, dealing 2 damage, and healing 2 hit points. 71 | 72 | -- Boss turn -- 73 | - Player has 3 hit points, 7 armor, 239 mana 74 | - Boss has 12 hit points 75 | Shield's timer is now 3. 76 | Recharge provides 101 mana; its timer is now 0. 77 | Recharge wears off. 78 | Boss attacks for 8 - 7 = 1 damage! 79 | 80 | -- Player turn -- 81 | - Player has 2 hit points, 7 armor, 340 mana 82 | - Boss has 12 hit points 83 | Shield's timer is now 2. 84 | Player casts Poison. 85 | 86 | -- Boss turn -- 87 | - Player has 2 hit points, 7 armor, 167 mana 88 | - Boss has 12 hit points 89 | Shield's timer is now 1. 90 | Poison deals 3 damage; its timer is now 5. 91 | Boss attacks for 8 - 7 = 1 damage! 92 | 93 | -- Player turn -- 94 | - Player has 1 hit point, 7 armor, 167 mana 95 | - Boss has 9 hit points 96 | Shield's timer is now 0. 97 | Shield wears off, decreasing armor by 7. 98 | Poison deals 3 damage; its timer is now 4. 99 | Player casts Magic Missile, dealing 4 damage. 100 | 101 | -- Boss turn -- 102 | - Player has 1 hit point, 0 armor, 114 mana 103 | - Boss has 2 hit points 104 | Poison deals 3 damage. This kills the boss, and the player wins. 105 | You start with 50 hit points and 500 mana points. The boss's actual stats are in your puzzle input. What is the least amount of mana you can spend and still win the fight? (Do not include mana recharge effects as "spending" negative mana.) 106 | 107 | -------------------------------------------------------------------------------- /22/problem-b.md: -------------------------------------------------------------------------------- 1 | On the next run through the game, you increase the difficulty to hard. 2 | 3 | At the start of each player turn (before any other effects apply), you lose 1 hit point. If this brings you to or below 0 hit points, you lose. 4 | 5 | With the same starting stats for you and the boss, what is the least amount of mana you can spend and still win the fight? 6 | 7 | -------------------------------------------------------------------------------- /23/Makefile: -------------------------------------------------------------------------------- 1 | ../Soln/Makefile.template -------------------------------------------------------------------------------- /23/README.md: -------------------------------------------------------------------------------- 1 | Just build a machine simulator and run it. This could be a 2 | speed-programming exercise, but I went for elegance. 3 | 4 | The originally-published version of this code contained the 5 | "[Existential Typeclass Antipattern](https://lukepalmer.wordpress.com/2010/01/24/haskell-antipattern-existential-typeclass/)" 6 | in a big way. Cleaning it up resulted in much smaller and 7 | more readable code while sacrificing nothing much. Recursion 8 | was also removed via `iterate`. 9 | 10 | The simulator should be quite extensible for other kinds of 11 | instructions and machines; I thought that might be important 12 | for Part B, but it wasn't. 13 | -------------------------------------------------------------------------------- /23/input.txt: -------------------------------------------------------------------------------- 1 | jio a, +19 2 | inc a 3 | tpl a 4 | inc a 5 | tpl a 6 | inc a 7 | tpl a 8 | tpl a 9 | inc a 10 | inc a 11 | tpl a 12 | tpl a 13 | inc a 14 | inc a 15 | tpl a 16 | inc a 17 | inc a 18 | tpl a 19 | jmp +23 20 | tpl a 21 | tpl a 22 | inc a 23 | inc a 24 | tpl a 25 | inc a 26 | inc a 27 | tpl a 28 | inc a 29 | tpl a 30 | inc a 31 | tpl a 32 | inc a 33 | tpl a 34 | inc a 35 | inc a 36 | tpl a 37 | inc a 38 | inc a 39 | tpl a 40 | tpl a 41 | inc a 42 | jio a, +8 43 | inc b 44 | jie a, +4 45 | tpl a 46 | inc a 47 | jmp +2 48 | hlf a 49 | jmp -7 50 | -------------------------------------------------------------------------------- /23/problem-a.md: -------------------------------------------------------------------------------- 1 | Little Jane Marie just got her very first computer for 2 | Christmas from some unknown benefactor. It comes with 3 | instructions and an example program, but the computer itself 4 | seems to be malfunctioning. She's curious what the program 5 | does, and would like you to help her run it. 6 | 7 | The manual explains that the computer supports two registers 8 | and six instructions (truly, it goes on to remind the 9 | reader, a state-of-the-art technology). The registers are 10 | named a and b, can hold any non-negative integer, and begin 11 | with a value of 0. The instructions are as follows: 12 | 13 | * `hlf r` sets register r to half its current value, then 14 | continues with the next instruction. 15 | * `tpl r` sets register r to triple its current value, then 16 | continues with the next instruction. 17 | * `inc r` increments register r, adding 1 to it, then 18 | continues with the next instruction. 19 | * `jmp offset` is a jump; it continues with the instruction 20 | offset away relative to itself. 21 | * `jie r, offset` is like jmp, but only jumps if register r 22 | is even ("jump if even"). 23 | * `jio r, offset` is like jmp, but only jumps if register r 24 | is 1 ("jump if one", not odd). 25 | 26 | All three jump instructions work with an offset relative to 27 | that instruction. The offset is always written with a 28 | prefix + or - to indicate the direction of the jump (forward 29 | or backward, respectively). For example, `jmp +1` would simply 30 | continue with the next instruction, while `jmp +0` would 31 | continuously jump back to itself forever. 32 | 33 | The program exits when it tries to run an instruction beyond 34 | the ones defined. 35 | 36 | For example, this program sets a to 2, because the jio 37 | instruction causes it to skip the tpl instruction: 38 | 39 | inc a 40 | jio a, +2 41 | tpl a 42 | inc a 43 | 44 | What is the value in register b when the program in your 45 | puzzle input is finished executing? 46 | 47 | -------------------------------------------------------------------------------- /23/problem-b.md: -------------------------------------------------------------------------------- 1 | The unknown benefactor is very thankful for releasi-- er, 2 | helping little Jane Marie with her computer. Definitely not 3 | to distract you, what is the value in register b after the 4 | program is finished executing if register a starts as 1 5 | instead? 6 | -------------------------------------------------------------------------------- /23/soln.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | import Soln 7 | 8 | -- | Give registers symbolic names for type safety. 9 | data Reg = RegA | RegB 10 | 11 | -- | State of the machine. 12 | data State = State { 13 | rA, rB, pc :: Int } 14 | deriving Show 15 | 16 | -- | Execute an ALU instruction. 17 | updateALU :: (Int -> Int) -> Reg -> State -> State 18 | updateALU fn RegA state = 19 | state { rA = fn (rA state), 20 | pc = pc state + 1 } 21 | updateALU fn RegB state = 22 | state { rB = fn (rB state), 23 | pc = pc state + 1 } 24 | 25 | -- | Execute a control instruction. 26 | updateCtl :: Int -> State -> State 27 | updateCtl off state = 28 | state { pc = pc state + off } 29 | 30 | -- | Execute a conditional instruction. 31 | updateCond :: (Int -> Bool) -> Reg -> Int -> State -> State 32 | updateCond fn reg off state = 33 | case fn (readReg reg state) of 34 | True -> state { pc = pc state + off } 35 | False -> state { pc = pc state + 1 } 36 | where 37 | readReg :: Reg -> State -> Int 38 | readReg RegA s = rA s 39 | readReg RegB s = rB s 40 | 41 | -- | Parse and genericize a program's instructions. 42 | parseInsn :: [String] -> State -> State 43 | parseInsn ["hlf", reg] = 44 | updateALU (`div` 2) (parseReg reg) 45 | parseInsn ["inc", reg] = 46 | updateALU (+ 1) (parseReg reg) 47 | parseInsn ["tpl", reg] = 48 | updateALU (* 3) (parseReg reg) 49 | parseInsn ["jmp", off] = 50 | updateCtl (parseOff off) 51 | parseInsn ["jie", reg, off] | last reg == ',' = 52 | updateCond even (parseReg (init reg)) (parseOff off) 53 | parseInsn ["jio", reg, off] | last reg == ',' = 54 | updateCond (== 1) (parseReg (init reg)) (parseOff off) 55 | parseInsn _ = error "illegal instruction" 56 | 57 | -- | Parse a register name. 58 | parseReg :: String -> Reg 59 | parseReg "a" = RegA 60 | parseReg "b" = RegB 61 | parseReg _ = error "illegal register" 62 | 63 | -- | Parse an offset, paying careful attention to 64 | -- the required sign. 65 | parseOff :: String -> Int 66 | parseOff ('+' : off) = read off 67 | parseOff ('-' : off) = negate (read off) 68 | parseOff _ = error "illegal offset" 69 | 70 | -- | Parse a whole program. 71 | parseProgram :: String -> [State -> State] 72 | parseProgram stuff = 73 | map (parseInsn . words) $ lines stuff 74 | 75 | -- | Run given program from given starting state. 76 | -- 77 | -- The use of `!!` here is not going to win any efficiency 78 | -- prizes. An array would be a better plan, but Haskell 79 | -- arrays are a pain and it doesn't matter for short runs of 80 | -- small programs. 81 | runProgram :: [State -> State] -> State -> State 82 | runProgram insns state0 = 83 | case find terminated $ iterate runInsn state0 of 84 | Just state -> state 85 | Nothing -> error "program terminated early" 86 | where 87 | terminated state = pc state >= length insns 88 | runInsn state 89 | | pc state < 0 = error "pc off top" 90 | | terminated state = state 91 | | otherwise = 92 | (insns !! pc state) state 93 | 94 | solna :: String -> IO () 95 | solna stuff = do 96 | print $ runProgram (parseProgram stuff) $ State 0 0 0 97 | 98 | solnb :: String -> IO () 99 | solnb stuff = do 100 | print $ runProgram (parseProgram stuff) $ State 1 0 0 101 | 102 | main :: IO () 103 | main = makeMain solna solnb 104 | -------------------------------------------------------------------------------- /24/Makefile: -------------------------------------------------------------------------------- 1 | ../Soln/Makefile.template -------------------------------------------------------------------------------- /24/README.md: -------------------------------------------------------------------------------- 1 | N-way Number Partitioning is a classic NP-complete 2 | problem. Rich Korf has a couple of great papers on efficient 3 | solution for reasonable-sized hard instances. Unfortunately, 4 | we need to examine all solutions, so efficiency in general 5 | is not too likely. 6 | 7 | The first Haskell solution I tried involved examining all 8 | possible packings, with some really incomplete symmetry 9 | breaking to speed things up a bit. It had bad runtimes: 10 | about 1 minute for Part A and 7 minutes for Part B. 11 | 12 | The solution here uses a more sophisticated strategy that 13 | takes advantage of the entanglement scoring. The key ideas 14 | are: 15 | 16 | * A small number of larger values with a given sum will 17 | usually have a smaller product than a large number of 18 | smaller values. (This is especially true for this problem, 19 | where all the packages have prime weight, but it is true 20 | in general as well.) Thus we can get a low-entanglement 21 | single bin by trying to jam all the largest values in the 22 | bin first. 23 | 24 | * If we examine all possible packings of a single bin first, 25 | we won't miss any solutions by extending the packing to 26 | the remaining bins. Thus, we can assume that we are 27 | packing a smallest-entanglement bin first. When the 28 | entanglement of that bin gets larger than the best bin we 29 | have examined so far, we can abandon the whole packing. 30 | 31 | The overall strategy becomes: 32 | 33 | 1. Find a starting packing with some score *e*. This is 34 | really fast even with dumb brute-force methods. 35 | 36 | 2. Using the observations above, try to pack a bin with an 37 | entanglement limit of *e* - 1 and then extend that bin 38 | packing to a packing of the full collection of packages. 39 | 40 | 3. If Step 2 fails, the packing we first found is 41 | minimal, so we are done. 42 | 43 | 4. Otherwise, we have found a better packing with a score 44 | *e'*, so start again at Step 2 with *e* = *e'*. 45 | 46 | With this strategy in place (after a little grief), the 47 | solution runs in about 50 milliseconds for Part A and 20 48 | milliseconds for Part B. Note that Part B went from being 7x 49 | slower to 3x faster than Part A: there are far fewer valid 50 | packings for a smaller bin size. Part B ended up with a 51 | speedup of about 20,000x. 52 | 53 | This solution makes major use of laziness. The Haskell 54 | notionally produces a complete list of all possible packings 55 | (including duplicates because of symmetry). However, only a 56 | small portion of this space is ever examined, and this is 57 | the only portion that is generated. One would have to write 58 | in a substantially different style in a strict language, and 59 | the resulting code would be much less versatile unless 60 | engineering carefully. 61 | 62 | I had a couple of small bugs, but for the most part the 63 | first code I typed in for this worked. The type system was a 64 | huge help here, as was the functional programming 65 | decomposition of cases. 66 | 67 | The `loop` function invoked here is something I stuck in 68 | `Soln`: it iterates an `Either` monad on its `Right` output 69 | until it finally returns `Left`, at which point this value 70 | becomes the result. 71 | -------------------------------------------------------------------------------- /24/input.txt: -------------------------------------------------------------------------------- 1 | 1 2 | 3 3 | 5 4 | 11 5 | 13 6 | 17 7 | 19 8 | 23 9 | 29 10 | 31 11 | 37 12 | 41 13 | 43 14 | 47 15 | 53 16 | 59 17 | 67 18 | 71 19 | 73 20 | 79 21 | 83 22 | 89 23 | 97 24 | 101 25 | 103 26 | 107 27 | 109 28 | 113 29 | -------------------------------------------------------------------------------- /24/problem-a.md: -------------------------------------------------------------------------------- 1 | It's Christmas Eve, and Santa is loading up the sleigh for 2 | this year's deliveries. However, there's one small problem: 3 | he can't get the sleigh to balance. If it isn't balanced, he 4 | can't defy physics, and nobody gets presents this year. 5 | 6 | No pressure. 7 | 8 | Santa has provided you a list of the weights of every 9 | package he needs to fit on the sleigh. The packages need to 10 | be split into three groups of exactly the same weight, and 11 | every package has to fit. The first group goes in the 12 | passenger compartment of the sleigh, and the second and 13 | third go in containers on either side. Only when all three 14 | groups weigh exactly the same amount will the sleigh be able 15 | to fly. Defying physics has rules, you know! 16 | 17 | Of course, that's not the only problem. The first group - 18 | the one going in the passenger compartment - needs as few 19 | packages as possible so that Santa has some legroom left 20 | over. It doesn't matter how many packages are in either of 21 | the other two groups, so long as all of the groups weigh the 22 | same. 23 | 24 | Furthermore, Santa tells you, if there are multiple ways to 25 | arrange the packages such that the fewest possible are in 26 | the first group, you need to choose the way where the first 27 | group has the smallest quantum entanglement to reduce the 28 | chance of any "complications". The quantum entanglement of a 29 | group of packages is the product of their weights, that is, 30 | the value you get when you multiply their weights 31 | together. Only consider quantum entanglement if the first 32 | group has the fewest possible number of packages in it and 33 | all groups weigh the same amount. 34 | 35 | For example, suppose you have ten packages with weights 1 36 | through 5 and 7 through 11. For this situation, the unique 37 | first groups, their quantum entanglements, and a way to 38 | divide the remaining packages are as follows: 39 | 40 | Group 1; Group 2; Group 3 41 | 11 9 (QE= 99); 10 8 2; 7 5 4 3 1 42 | 10 9 1 (QE= 90); 11 7 2; 8 5 4 3 43 | 10 8 2 (QE=160); 11 9; 7 5 4 3 1 44 | 10 7 3 (QE=210); 11 9; 8 5 4 2 1 45 | 10 5 4 1 (QE=200); 11 9; 8 7 3 2 46 | 10 5 3 2 (QE=300); 11 9; 8 7 4 1 47 | 10 4 3 2 1 (QE=240); 11 9; 8 7 5 48 | 9 8 3 (QE=216); 11 7 2; 10 5 4 1 49 | 9 7 4 (QE=252); 11 8 1; 10 5 3 2 50 | 9 5 4 2 (QE=360); 11 8 1; 10 7 3 51 | 8 7 5 (QE=280); 11 9; 10 4 3 2 1 52 | 8 5 4 3 (QE=480); 11 9; 10 7 2 1 53 | 7 5 4 3 1 (QE=420); 11 9; 10 8 2 54 | 55 | Of these, although 10 9 1 has the smallest quantum 56 | entanglement (90), the configuration with only two packages, 57 | 11 9, in the passenger compartment gives Santa the most 58 | legroom and wins. In this situation, the quantum 59 | entanglement for the ideal configuration is 60 | therefore 99. Had there been two configurations with only 61 | two packages in the first group, the one with the smaller 62 | quantum entanglement would be chosen. 63 | 64 | What is the quantum entanglement of the first group of 65 | packages in the ideal configuration? 66 | 67 | -------------------------------------------------------------------------------- /24/problem-b.md: -------------------------------------------------------------------------------- 1 | That's weird... the sleigh still isn't balancing. 2 | 3 | "Ho ho ho", Santa muses to himself. "I forgot the trunk". 4 | 5 | Balance the sleigh again, but this time, separate the 6 | packages into four groups instead of three. The other 7 | constraints still apply. 8 | 9 | Given the example packages above, this would be the new 10 | unique first groups, their quantum entanglements, and one 11 | way to divide the remaining packages: 12 | 13 | 14 | 11 4 (QE=44); 10 5; 9 3 2 1; 8 7 15 | 10 5 (QE=50); 11 4; 9 3 2 1; 8 7 16 | 9 5 1 (QE=45); 11 4; 10 3 2; 8 7 17 | 9 4 2 (QE=72); 11 3 1; 10 5; 8 7 18 | 9 3 2 1 (QE=54); 11 4; 10 5; 8 7 19 | 8 7 (QE=56); 11 4; 10 5; 9 3 2 1 20 | 21 | Of these, there are three arrangements that put the minimum 22 | (two) number of packages in the first group: 11 4, 10 5, and 23 | 8 7. Of these, 11 4 has the lowest quantum entanglement, and 24 | so it is selected. 25 | 26 | Now, what is the quantum entanglement of the first group of 27 | packages in the ideal configuration? 28 | 29 | -------------------------------------------------------------------------------- /24/soln.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | import Soln 7 | 8 | -- | Get the list of weights. 9 | parseWeights :: String -> [Int] 10 | parseWeights stuff = 11 | map read $ lines stuff 12 | 13 | -- | A bin, with its current total weight and entanglement tracked. 14 | data Bin = Bin { 15 | binWeight :: Int, 16 | binEntanglement :: Integer, 17 | binPackages :: [Int] } 18 | 19 | -- | Insert a package into a bin. 20 | insertPackage :: Int -> Bin -> Bin 21 | insertPackage p bin = 22 | Bin { binWeight = p + binWeight bin, 23 | binEntanglement = fromIntegral p * binEntanglement bin, 24 | binPackages = p : binPackages bin } 25 | 26 | -- | The empty bin. 27 | emptyBin :: Bin 28 | emptyBin = Bin 0 1 [] 29 | 30 | -- | Pack up packages into bins. Return a list of packings 31 | -- consisting of the target weight packing and the remaining 32 | -- unpacked packages. 33 | packBin :: Maybe Integer -> Int -> [Int] -> [([Int], [Int])] 34 | packBin maxEntanglement targetWeight toPack = 35 | catMaybes $ map getSolution $ 36 | foldr packPackage [(emptyBin, emptyBin)] toPack 37 | where 38 | totalWeight = sum toPack 39 | getSolution (packed, unpacked) 40 | | binWeight packed == targetWeight = 41 | Just (binPackages packed, binPackages unpacked) 42 | | otherwise = Nothing 43 | packPackage p partials = 44 | concatMap packPartial partials 45 | where 46 | packPartial (packed, unpacked) 47 | | p + packedWeight + remainingWeight < targetWeight = [] 48 | | excessiveEntanglement = [dontPack] 49 | | p + packedWeight > targetWeight = [dontPack] 50 | | otherwise = [doPack, dontPack] 51 | where 52 | excessiveEntanglement = 53 | case maxEntanglement of 54 | Nothing -> False 55 | Just e -> fromIntegral p * binEntanglement packed > e 56 | packedWeight = binWeight packed 57 | remainingWeight = totalWeight - binWeight unpacked 58 | doPack = (insertPackage p packed, unpacked) 59 | dontPack = (packed, insertPackage p unpacked) 60 | 61 | -- | Partition a collection of packages into `n` bins, each 62 | -- with equal weight, in every way possible. Constrain the 63 | -- packings so that at least one has limited entanglement. 64 | packBins :: Maybe Integer -> Int -> [Int] -> [[[Int]]] 65 | packBins maxEntanglement n packages = 66 | map complete $ (iterate (packNext Nothing) oneBin !! (n - 2)) 67 | where 68 | totalWeight = sum packages 69 | targetWeight 70 | | totalWeight `mod` n == 0 = totalWeight `div` n 71 | | otherwise = error "bad package parity" 72 | oneBin = packNext maxEntanglement [([], packages)] 73 | complete :: ([[Int]], [Int]) -> [[Int]] 74 | complete (partial, remaining) = 75 | remaining : partial 76 | packNext :: Maybe Integer -> [([[Int]], [Int])] -> [([[Int]], [Int])] 77 | packNext maxBin partials = 78 | concatMap packPartial partials 79 | where 80 | packPartial (packed, remaining) = 81 | map recordPacking $ packBin maxBin targetWeight remaining 82 | where 83 | recordPacking (packing, remaining') = 84 | (packing : packed, remaining') 85 | 86 | -- | Return a least-entanglement packing of the given 87 | -- packages. Strategy: keep trying packings with smaller 88 | -- scores until no possible packings remain. The last score 89 | -- must be minimal, by construction. 90 | bestPacking :: Int -> [Int] -> [[Int]] 91 | bestPacking n packages = 92 | loop improvePacking (undefined, Nothing) 93 | where 94 | improvePacking :: ([[Int]], Maybe Integer) 95 | -> Either [[Int]] ([[Int]], Maybe Integer) 96 | improvePacking (bestSoln, bestEntanglement) = 97 | case packBins (fmap (subtract 1) bestEntanglement) n packages of 98 | [] -> Left bestSoln 99 | (packing : _ ) -> Right (packing, Just (scorePacking packing)) 100 | 101 | -- | Return the entanglement of a bin. Note that this will in 102 | -- general be a product too big to fit in an 'Int'. 103 | entanglement :: [Int] -> Integer 104 | entanglement bin = 105 | product $ map fromIntegral bin 106 | 107 | -- | Return the entanglement of the least-entangled 108 | -- bin in a packing. 109 | scorePacking :: [[Int]] -> Integer 110 | scorePacking packing = 111 | minimum $ map entanglement packing 112 | 113 | soln :: Int -> String -> [[Int]] 114 | soln n stuff = do 115 | let packages = sort $ parseWeights stuff 116 | bestPacking n packages 117 | 118 | solna :: String -> IO () 119 | solna stuff = print $ scorePacking $ soln 3 stuff 120 | 121 | solnb :: String -> IO () 122 | solnb stuff = print $ scorePacking $ soln 4 stuff 123 | 124 | main :: IO () 125 | main = makeMain solna solnb 126 | -------------------------------------------------------------------------------- /25/Makefile: -------------------------------------------------------------------------------- 1 | ../Soln/Makefile.template -------------------------------------------------------------------------------- /25/README.md: -------------------------------------------------------------------------------- 1 | The tricky part of this problem is the index calculation. I 2 | am embarassed to say how long it took me to get this simple 3 | Gaussian sum correct. The strategy is to compute the 4 | diagonal to which the given row and column belong, and then 5 | walk up the diagonal to the correct position. 6 | 7 | My original Haskell solution was very short, had the input 8 | values hard-coded in, and ran in about 3 seconds. Someone on 9 | the Interwebs pointed out that modular exponentiation would 10 | be way faster, so I coded that for this solution. I also dug 11 | the row and column out of the input in a very kludgy 12 | way. The result runs in about a millisecond. 13 | -------------------------------------------------------------------------------- /25/input.txt: -------------------------------------------------------------------------------- 1 | To continue, please consult the code grid in the manual. 2 | Enter the code at row 3010, column 3019. 3 | -------------------------------------------------------------------------------- /25/problem.md: -------------------------------------------------------------------------------- 1 | Merry Christmas! Santa is booting up his weather machine; 2 | looks like you might get a white Christmas after all. 3 | 4 | The weather machine beeps! On the console of the machine is 5 | a copy protection message asking you to enter a code from 6 | the instruction manual. Apparently, it refuses to run unless 7 | you give it that code. No problem; you'll just look up the 8 | code in the-- 9 | 10 | "Ho ho ho", Santa ponders aloud. "I can't seem to find the 11 | manual." 12 | 13 | You look up the support number for the manufacturer and give 14 | them a call. Good thing, too - that 49th star wasn't going 15 | to earn itself. 16 | 17 | "Oh, that machine is quite old!", they tell you. "That model 18 | went out of support six minutes ago, and we just finished 19 | shredding all of the manuals. I bet we can find you the code 20 | generation algorithm, though." 21 | 22 | After putting you on hold for twenty minutes (your call is 23 | very important to them, it reminded you repeatedly), they 24 | finally find an engineer that remembers how the code system 25 | works. 26 | 27 | The codes are printed on an infinite sheet of paper, 28 | starting in the top-left corner. The codes are filled in by 29 | diagonals: starting with the first row with an empty first 30 | box, the codes are filled in diagonally up and to the 31 | right. This process repeats until the infinite paper is 32 | covered. So, the first few codes are filled in in this 33 | order: 34 | 35 | | 1 2 3 4 5 6 36 | ---+---+---+---+---+---+---+ 37 | 1 | 1 3 6 10 15 21 38 | 2 | 2 5 9 14 20 39 | 3 | 4 8 13 19 40 | 4 | 7 12 18 41 | 5 | 11 17 42 | 6 | 16 43 | 44 | For example, the 12th code would be written to row 4, column 45 | 2; the 15th code would be written to row 1, column 5. 46 | 47 | The voice on the other end of the phone continues with how 48 | the codes are actually generated. The first code 49 | is 20151125. After that, each code is generated by taking 50 | the previous one, multiplying it by 252533, and then keeping 51 | the remainder from dividing that value by 33554393. 52 | 53 | So, to find the second code (which ends up in row 2, column 54 | 1), start with the previous value, 20151125. Multiply it by 55 | 252533 to get 5088824049625. Then, divide that by 33554393, 56 | which leaves a remainder of 31916031. That remainder is the 57 | second code. 58 | 59 | "Oh!", says the voice. "It looks like we missed a scrap from 60 | one of the manuals. Let me read it to you." You write down 61 | his numbers: 62 | 63 | | 1 2 3 4 5 6 64 | ---+---------+---------+---------+---------+---------+---------+ 65 | 1 | 20151125 18749137 17289845 30943339 10071777 33511524 66 | 2 | 31916031 21629792 16929656 7726640 15514188 4041754 67 | 3 | 16080970 8057251 1601130 7981243 11661866 16474243 68 | 4 | 24592653 32451966 21345942 9380097 10600672 31527494 69 | 5 | 77061 17552253 28094349 6899651 9250759 31663883 70 | 6 | 33071741 6796745 25397450 24659492 1534922 27995004 71 | 72 | "Now remember", the voice continues, "that's not even all of 73 | the first few numbers; for example, you're missing the one 74 | at 7,1 that would come before 6,2. But, it should be enough 75 | to let your-- oh, it's time for lunch! Bye!" The call 76 | disconnects. 77 | 78 | Santa looks nervous. Your puzzle input contains the message 79 | on the machine's console. What code do you give the machine? 80 | 81 | -------------------------------------------------------------------------------- /25/soln.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | 7 | import Soln 8 | 9 | -- | PRNG parameters. 10 | prngA, prngM, prngSeed :: Word64 11 | prngA = 252533 12 | prngM = 33554393 13 | prngSeed = 20151125 14 | 15 | -- | Multiplication mod 'prngM' 16 | modTimes :: Word64 -> Word64 -> Word64 17 | modTimes x y = (x * y) `mod` prngM 18 | 19 | -- | Squaring mod 'prngM' 20 | modSquare :: Word64 -> Word64 21 | modSquare x = x `modTimes` x 22 | 23 | -- | 'prngA' to the given power, mod 'prngM' 24 | modExpA :: Int -> Word64 25 | modExpA e | e < 0 = error "negative exponent" 26 | modExpA 0 = 1 27 | modExpA 1 = prngA 28 | modExpA e | even e = modSquare $ modExpA $ e `div` 2 29 | modExpA e = prngA `modTimes` modExpA (e - 1) 30 | 31 | -- | Output of PRNG after given number of rounds. 32 | prngRounds :: Int -> Word64 33 | prngRounds rounds = 34 | prngSeed `modTimes` modExpA rounds 35 | 36 | -- | Calculate the index position of a given row and column. 37 | index :: (Int, Int) -> Int 38 | index (r, c) = (r + c) * (r + c + 1) `div` 2 + c 39 | 40 | -- | Dig the row and column positions out of the input. 41 | parsePosition :: String -> (Int, Int) 42 | parsePosition stuff = 43 | case separate numbers of 44 | [rs, cs] -> (read rs, read cs) 45 | _ -> error "mysterious input" 46 | where 47 | numbers = filter okChar stuff 48 | okChar c = isDigit c || c == ',' 49 | separate s = 50 | words $ map commaSpace s 51 | where 52 | commaSpace ',' = ' ' 53 | commaSpace c = c 54 | 55 | main :: IO () 56 | main = do 57 | stuff <- readFile "input.txt" 58 | let (r, c) = parsePosition stuff 59 | print $ prngRounds $ index (r - 1, c - 1) 60 | -------------------------------------------------------------------------------- /3/Makefile: -------------------------------------------------------------------------------- 1 | ../Soln/Makefile.template -------------------------------------------------------------------------------- /3/README.md: -------------------------------------------------------------------------------- 1 | The key to Part B of this problem is to re-use the machinery 2 | of Part A by using the fact that Santa and Robo-Santa are 3 | completely separate entities. 4 | 5 | This problem might better be done by keeping track of moves 6 | using `Data.Set` rather than lists. This approach would 7 | certainly be more efficient. In any case, the approach used 8 | illustrates the use of the somewhat obscure 9 | `Data.List.mapAccumL`. 10 | -------------------------------------------------------------------------------- /3/input.txt: -------------------------------------------------------------------------------- 1 | ^^<<>^^>^^^><^>v^>v><><><^^v>v^v>>>^<>v<^<^>>>>><>^>>^>v^>><<^>v>v<>^v^v^vvv><>^^>v><>^><^^^v>>^v^>v><>v^^><vv^<<>v>>><<<>>^^^vv>>>^><<<>><><^>v<>^>v<^v^><<<<>^<>v>^v>vv<^<<>>>>^^v>vv^^<>^<>^^^^<^^^vv<^^v^^>v>^v^^^^>><v<>^v^><>^^><<^^<^^>vv<>v^<^v^>^^>^<>v^^vv<>>v><<<>vvv<>v<>><^<^v<>^vv>^^v<^<>>vv<^>>^>>vv^v>^v^<>^>>>>vv>^^>v>vv>v><^vv^<^<<^^vv^^v>^>>v><^<>v<><>^^<>v>><>^^>^^>>vvv^><<<<<^<^vv<^<>^^^<<<^>^^^vv<>^<>v<^v>^<<>^v<>>v<<^<^<<<><><>^>>>>^>v^v<>vv<^vvv^^^^vv>^v^^v^<^vv<^vv>v<^>vv<>>^>^><^>v>^v>vvv<>^>^v<><>vv>><^v^<><>>v^v^><^<^>vv>v<^>vvv>v<<<<<^>^vv>^><><>^<v^>^><><>>^>^>><^^^>^^>^^v^^<^v^^>v^^>>><<><><>^^<<^^v^>v>><>^^^><^vvv<^^^^^v><<><><>>^>vv>>^vv^^><v<^^>^<^^<^>>>^v<>v<^^^>vvv^v<<^><>>>>v>>>^^vvv^vvv<^^^^v^v^^<<^>v^v^<<><>><^v><<>><<<>^v>v<>^^vv>>^<>v^^<<^v>>v<>>^v^^>><^>v^<^v^^>><>v^>^v^v<<v<><>vv>>>>^>v<>v<<<>^^>vv^v<>^<<<<>>^^>^v<>^v<>>^v^<<^<^>>>^vv<>v^>>v<^^v>>^>><<><<<>>>^v>><^^vv>><>v^><>vv<^^v^^^v<>><^vvv<<^<>v>>>v>><>>><>>^v>v>^^<^>^>v><>vv>^v><<>>>>>>>^<<^vv^^vvvv<^^><<vvv<>^><v<>>^^<<^^vv>v>^vv>>^v^^vvvv>^^>>v^v^^><<^>v>>^^>^<^^<>vvv^vv>v>^v<><^vv^>^v>>>^^<^<^>^v^>^>>>^v>^>^^^>>^<>v^^<>^v<<^^>^^v<^v^>><^v^>^<>>^vv^vv^>v^><^^<^v<^><>v><^v^v^^^v>v^<>^<^^>^v^^<>v^<<>>vv<>>>>v>v<>^>>>v<>^^>^<^><>^><><>^<<>>><<^>^vv^v>>vv^<<^^<<><<^v^>>>v<<<v>^vv<^v>v<^>^^vv>v>><>><>^<>><><<^<<^v^v<v>vvv<^v^^^v^><^v>^<^>^<<>v^<><>>^v<>vvv<^>><^^>^>^v^vv<^><<^v>><^^v>^v<>^>vvvv><^>^<<>v>^>>^<^<<<^v^^^>^>>^>><><<^>v^^>v<<<^>vvv^^<<><^v^v^^^>^^>^vv<>v>>v^>vv^vv>v<^v^^>>^v^v<>>^^><><>>>^>^<>^^v^^><^<>><<^>vv^>>>v<<><<^>vv>vvv>^<><>>>>vv><<><<<<>><v>v^><>v^v^^><>v>v>^^v<^v<>>^^^^^>^^>v<^<^>>>^><^^>><<>>^><>^^^>v^^^>^^v^<>^^><^>>><><^>>vv<^>v<^v>v^<^vv^^><<<><><^v^v>v^>>^^vv^^v>^<^v<>^>^><^^v><^<^<>v^^>^><>>><<<><>v<<^v^^<^><>^<><>v<^^>^^<<>>^><^><^<^>^^v<>v>>><><<>^>v><><<<>^^^v>><<^v>^>>>>^vv<^<>>^<^^<^v>v^<<^<<<<<^<^>>^><<>><>v^v>^<^>v^<>^v^v^v><^vv<<^<>^^^<>^v>^v<>>^>v<<>v<>v^v>v<<<>>v>vv>>v<<>v<>v<^>^>^>v>^>^^^v<<>>>^vvv^^>^^<^vv^^^^>v>^v^>v^^v^>>^v>^vv>^^v^<<<<>^<><^<^<<^^>v^^^v<>>vvv>vv>^<^v>>^v<^^v^v>v<>^v<<<^^v^^^<^v>v^v^v>>v<>^v>vv^v>vv<<^v^v>v>><^vv>>>><<<><>^v^<^vvv>v<>><^v>^>>vv<><><>v><>>><^>vv>>^<>v^>>^><<<^><<>^v^>>><><>vv>^<>^>^v^^><^>>><<>v^<^vv>^<^vv>>vv<><<^><>v<^^<^>vv^^^^vv<<>vv<>v<>>>>^><>^<><>v<>><<>^^vvv>^^^<><>>vvv^v>><>vv^^^v^<<>^^v<><<^^v<>^^>^<^^v>>v^v^^>>v>>>^<<^<>^>^^v>>>>^v<<<^^vv><^>vv<>>vv^>v>>v^vvv^^>vv^<v^>>v^<>>><><<^^<^v>^>>>v>v>^v<>vv>v>^v<<<>><<><><>v^>>>v^>v^>>vv^^^<>>><^>v^<>^^>v<><<<>v^v>^>v<^<>v>v^^>>v>vv^v<>>^^^^<>v^>>>>>>>>^v<^<<>>><<<^<<^>^>v^<>^<<<>v>><^vv^>^>^>>>^v<<>^>^v^><>>v^>v^>^>>v<>vv^v<<>^^>>vv<>vv>>^v<^vv>^v>v<>v^<><>v^^><<<><>^>^v^<>>v^v>v<>>^^<<^<^^vv^<>>^vv^<>>^^^^v>v><^^^v^<<<>^<^<<>><>>v<<^v^>><>>^vv^v>vv>>>>>>^^<<>v^>v^v>^^>>>^v>>^^^<>><>v^<<v>v^^^>^v>^v<^<<><>vv>^^^<^^vv^^>vv>v<<^>^vv><^>^^^^v<v^<<^^>>^^vvvv^v^>vv>>v^vvv<>>^><>>v^^>>^<>>vvvv^>>>v<<^<<^>v^>><<v>v^>^v><>v<<>vv>>><^>>^^v>^>><>vv^><<>>vv<<<^<^^>^<<^>>>>>v>vv<^>^v><>>vv^vvvv>v^>>v><<^^^v>>vv^^>v>^v>^v^^>^<^vvvv<<^>>^<<^^>>^<^>v^><^vv>^^v>>><>v^v>^v<^><<<>vv>v<><>>v^<>^^>^<>^<<^>>vv^><^>v^>>v^>v>vv><>>v<^>><<vvv^vvv^vv^>^>v>>>>vv^>^<>v<^>^<^v>vv<^<<>>^<^<^^<>^<v<<>v>><^v<<^vvv>v>v<<^^<^^>v^^^>^>vv^^^vv>v<>>>vv>><><^><><<>vv>vv^v^>>><>v>>vv>^^vvv^>^^>^>^>^v<<^vv^>vvv^^vv><^>^v^>^><>v<^^vv<^<>>^^v^v>v^vv<>><^v>^<^v>^<>^v>>>><>>>v><^v^vv><<^v<<>^^<^v>vvv<><^^><<^v><>^<^v<^^<^vvvv^^>>>>vv>v>>>v<<<>v^>>vv^vvv<>vvv>>>><>>><>^v>><>>^vv<<^^vv><^v^vv^^^vv>^><^vvv<<>^vvv^>>>^<<<><<<<<^v<^^>>>>^>^v<<<^<^>>v^<<><<^^vvv^>v<>>^^>v>^v>>v>>>^<^<^>v^v^>><>^<<^vvv^^<>^v^>^^<<^>^vv>>v^v^>v>^<^^<>^>^>>>^^vvv^<<>v^<<>><>v<^<^>v^>^vv>^>>^<^v^<<<<^v^>v^><<<><^^^^>v>^^>v><>>^><<><^<>>^^>vv<^><^v^>>>vvv<^<>>^>>^v^<^^v>^^^v<^vv^>>^v><<^<><>>^>vv<<>^^^v^^><>>vv>v^>vvv^^v>^>>^>>v^<<v^<^v^vv^><^<^v<v>^v^<<^^>>^^^v>>>><^^v^>>^^>>^v^<^v>v^v^v^v^>v^vv<><>^^<>^><^^^<<<^v<<>^<^^^^^v^<^<<^^>^vv<>v^>><>>^>v>v<>^>v>><>^<>>>^>^>>v^>v><^vv^>v<v<><^><^v<<>v<>^^><<>v>vv<^vvv><><>vv^<<>^>^<^>>>^v>v<^v^^^vv<>>>^<<^>>><<^^v^>v^<^v>vvv>v^^vv>^^>>v<>^<<>^<><^^v^>><>^>v>>^^^<<^^v<>^^>^<>^>><^>^vvv><^>^<^>^>>vv<^>>^v>>^<>>^^>>>v^>v<>v^^vv>v><^v^^>v<<>v^^<><>^>vvv><^^^>^v^>v>>^vvv<^vv>^^>^>>v<>><<^v<^><>vv^<<^^vv>>^<^><^^v^<<>^v^^>v^>>^^^<^vv>v^>>>vv<<>v>>>^>v^^>v^<<>>vv<<^v>v<<^^>v>>v>v^>>^>>v>^><<^<<>^v>><^^<^<<^>vv<<>^<>^vv>^^^v<^v>vv>^^^^>v>v><<^<<<^vv><^<<<>>v<v>^v^v^<^<^vv>vvv<^^v<>v<<<<>v^<<><<<>v<^>^^v<^^v^>vv>vvv>v>>^><^>>v<v<<^^^v<<^v^^><><<<><<>v>^<<>v<<<^v>>v>><<^<><^v^^v^>^>vvvv<<><<>>^^^>v>v^><>>><^><<><<<^<>v^>>^v^>v^<>>v>^^><^<^v^>v>^vvv<>>v<>^vvvv><<<<<<<v<<<<^v<<><^<<>vv^<<>><^^<<>>>vv>>>>>>^v>v^v^^><<^v^^^<>^>>><>v^v^vvv^>>v>>>^^<<^^vv><<<^^^<<<^^>>>>vvv^v<^>^^>v<^<>v>>>^vv<<^^v^>^>^v>v>v^v^>v<><>>>>><<^v^<>^v<>vvv^>v>v^<><><>^>>><>^>^^<>v^^>^><>><>v^v^^v>>>>vv>>^v<<^v^<>^>v^^>^^<^><<<^^^v^^^^v<^<>v<^^<>vv^^v^<>^<<^>>v>v<<<^^^^vvv^<^<><>v<>><<><<^^^^vv><<>>>^v<<>^>>>v^>v>^^<>^<^>v>^>>>><>^^>v^^v>^vv^^v^><<<>>v<>v<<<>^<^<<>v>>>>^<vvv<^><^<<^>v>>v><>^>>>^v^v>v^^vv^>^<^^>>^><^vv^^vv^<>>^^^^<^^><>>^>>^>^vvv<^<^><>>>^^<><><<>>>>^<<>>>^<^v^>><<^>>>^<^>><>^^<>^v^^vv<><^>vv^^v^<^^^v^vvv^>><>>v<>^<^vvv<<^^>vv^^<<>>><^^vvv<<<^>^<><^>vv^><^<<>vv<>vv>v>v^<<>^^^^v^^^^^<<^><><^^v^>v>^>><^><<>v^>>^vvv>>^<^<>^^v^vv^^v><>>v<<<>v>^<>v<<>v^>^<<><<>v^>v<><^^>^<^v^^><^>vv>^>vvvv>^^><<>vv^>^v<<^<<^<<>vv>>^>>>>>v^v<^v>v>^^^vv^v<^<>v><>>vv>v><>v>^v<><<<<<>v^vv<<<<^<>^>><>^^vv>^<^<<>vv>>vv><>><^^><^<>^><>v^^^^v^^vv<>v<>v>^vv^>^<>^^^>v^>>>v><<^>>v<^v<>^^v<><<>v<^<^>v<>v^>v>^^<<<^^vv^<><<<>>v>^^<>v>>>><<>v^v<>v>><<<<^<<^>^>v^vv^><^v^^<>^^><>vv>^>vvv<^v^>>^>^>^^<<^>^>^v><>>^<^^v>^>>^^<><>>>^^>^^vvv>v<^^<>v^v^^v^v>><<^^^>>v>^vv>^>^^v<>^^<>v^^<>v^><<>vv<<^vvvv><<v>v^>v^<>v^>^^<>^>^^v<>><<<>^v^^v^v<<<^v^<>^<>v>^^>vv>^^<<<><<^>v<^^<^<<>^>>>>>^v^v<vvv<<>v>v>>^v^v^>><<<<>v^<<>>>^>>^>>< -------------------------------------------------------------------------------- /3/problem-a.md: -------------------------------------------------------------------------------- 1 | Santa is delivering presents to an infinite two-dimensional 2 | grid of houses. 3 | 4 | He begins by delivering a present to the house at his 5 | starting location, and then an elf at the North Pole calls 6 | him via radio and tells him where to move next. Moves are 7 | always exactly one house to the north (`^`), south (`v`), east 8 | (`>`), or west (`<`). After each move, he delivers another 9 | present to the house at his new location. 10 | 11 | However, the elf back at the north pole has had a little too 12 | much eggnog, and so his directions are a little off, and 13 | Santa ends up visiting some houses more than once. How many 14 | houses receive at least one present? 15 | 16 | For example: 17 | 18 | * `>` delivers presents to 2 houses: one at the starting 19 | location, and one to the east. 20 | * `^>v<` delivers presents to 4 houses in a square, 21 | including twice to the house at his starting/ending 22 | location. 23 | * `^v^v^v^v^v` delivers a bunch of presents to some very 24 | lucky children at only 2 houses. 25 | -------------------------------------------------------------------------------- /3/problem-b.md: -------------------------------------------------------------------------------- 1 | The next year, to speed up the process, Santa creates a 2 | robot version of himself, Robo-Santa, to deliver presents 3 | with him. 4 | 5 | Santa and Robo-Santa start at the same location (delivering 6 | two presents to the same starting house), then take turns 7 | moving based on instructions from the elf, who is 8 | eggnoggedly reading from the same script as the previous 9 | year. 10 | 11 | This year, how many houses receive at least one present? 12 | 13 | For example: 14 | 15 | * `^v` delivers presents to 3 houses, because Santa goes 16 | north, and then Robo-Santa goes south. 17 | * `^>v<` now delivers presents to 3 houses, and Santa and 18 | Robo-Santa end up back where they started. 19 | * `^v^v^v^v^v` now delivers presents to 11 houses, with 20 | Santa going one direction and Robo-Santa going the other. 21 | -------------------------------------------------------------------------------- /3/soln.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | 7 | import Soln 8 | 9 | -- | Process a move command. 10 | move :: (Int, Int) -> Char -> (Int, Int) 11 | move (x, y) c = 12 | case c of 13 | '^' -> (x, y + 1) 14 | 'v' -> (x, y - 1) 15 | '>' -> (x + 1, y) 16 | '<' -> (x - 1, y) 17 | _ -> error "bad command" 18 | 19 | -- | Get all the locations visited by a sequence of move 20 | -- commands. The use of `mapAccumL` accumulates every 21 | -- location visited in order. 22 | getLocs :: String -> [(Int, Int)] 23 | getLocs stuff = 24 | snd $ mapAccumL moveAndReport (0, 0) stuff 25 | where 26 | moveAndReport posn command = 27 | let posn' = move posn command in 28 | (posn', posn') 29 | 30 | -- | Strategy: Find all visited locations, filter out 31 | -- duplicates, and find the length of the resulting 32 | -- sequence. 33 | solna :: String -> IO () 34 | solna stuff = do 35 | print $ length $ nub $ getLocs stuff 36 | 37 | -- | Split the sequence of moves up into a sequence 38 | -- of odd-numbered moves and a sequence of even-numbered 39 | -- moves. This construction is a bit forced, but the 40 | -- obvious alternative requires explicit recursion. 41 | alternates :: String -> (String, String) 42 | alternates stuff = 43 | let (o, e) = partition (\(n, _) -> odd n) $ 44 | zip [(1 :: Integer)..] stuff in 45 | (map snd o, map snd e) 46 | 47 | -- | Strategy: Split out the moves for Santa and Robo-Santa, 48 | -- accumulate both traces, and then merge and de-duplicate 49 | -- them to get a list of all locations visited, and find the 50 | -- length of that list. 51 | solnb :: String -> IO () 52 | solnb stuff = do 53 | let (cmdsOdd, cmdsEven) = alternates stuff 54 | let locsOdd = getLocs cmdsOdd 55 | let locsEven = getLocs cmdsEven 56 | print $ length $ nub $ locsOdd ++ locsEven 57 | 58 | main :: IO () 59 | main = makeMain solna solnb 60 | -------------------------------------------------------------------------------- /4/Makefile: -------------------------------------------------------------------------------- 1 | ../Soln/Makefile.template -------------------------------------------------------------------------------- /4/README.md: -------------------------------------------------------------------------------- 1 | There is no way to do this one but by brute force, I think. 2 | 3 | This is one of the few cases in which a Hackage package is a 4 | more-or-less essential part of the solution. Install 5 | `cryptohash` from Hackage to get MD5. 6 | 7 | Some care has been taken to avoid Unicode issues. Haskell's 8 | `Char` datatype is Unicode, as it should be. So we use 9 | `Data.ByteString` for the hashes themselves, and 10 | `Data.ByteString.Char8` only for the input string. The 11 | result is that this code should work fine with an ISO-8801 12 | input password, but will probably work not so good for 13 | Unicode. One could fix this fairly straightforwardly to 14 | support Unicode passwords, but this seemed like an odd thing 15 | to do. 16 | 17 | This code is reasonably efficient, printing the Part B 18 | answer in about 1.6s. It could be made more efficient by 19 | working directly with the MD5 output and the input 20 | `ByteString` rather than a bunch of conversions. 21 | -------------------------------------------------------------------------------- /4/input.txt: -------------------------------------------------------------------------------- 1 | ckczppom -------------------------------------------------------------------------------- /4/problem-a.md: -------------------------------------------------------------------------------- 1 | Santa needs help mining some AdventCoins (very similar to 2 | bitcoins) to use as gifts for all the economically 3 | forward-thinking little girls and boys. 4 | 5 | To do this, he needs to find MD5 hashes which, in 6 | hexadecimal, start with at least five zeroes. The input to 7 | the MD5 hash is some secret key (your puzzle input, given 8 | below) followed by a number in decimal. To mine AdventCoins, 9 | you must find Santa the lowest positive number (no leading 10 | zeroes: 1, 2, 3, ...) that produces such a hash. 11 | 12 | For example: 13 | 14 | * If your secret key is abcdef, the answer is `609043`, 15 | because the MD5 hash of `abcdef609043` starts with five 16 | zeroes (`000001dbbfa`...), and it is the lowest such number 17 | to do so. 18 | 19 | * If your secret key is pqrstuv, the lowest number it 20 | combines with to make an MD5 hash starting with five 21 | zeroes is `1048970`; that is, the MD5 hash of pqrstuv1048970 22 | looks like `000006136ef`... . 23 | -------------------------------------------------------------------------------- /4/problem-b.md: -------------------------------------------------------------------------------- 1 | Now find one that starts with six zeroes. 2 | 3 | -------------------------------------------------------------------------------- /4/soln.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | 7 | import Crypto.Hash.MD5 8 | import qualified Data.ByteString as B 9 | import qualified Data.ByteString.Char8 as BC 10 | 11 | import Soln 12 | 13 | -- | Return the hex representation of a byte. There 14 | -- are other ways, but this is fast and easy to understand. 15 | byteHex :: Word8 -> String 16 | byteHex b = 17 | map intToDigit [ fromIntegral b `shiftR` 4, 18 | fromIntegral b .&. 0xf ] 19 | 20 | -- | Return the hex representation of a list of bytes. 21 | showHex :: [Word8] -> String 22 | showHex bs = concatMap byteHex bs 23 | 24 | -- | Return the hex representation of the 25 | -- MD5 hash of the given input string. 26 | hashString :: String -> String 27 | hashString s = 28 | showHex $ B.unpack $ hash $ BC.pack s 29 | 30 | -- | Strategy: Brute force. 31 | solve :: String -> Int -> Int 32 | solve stuff digits = 33 | fromJust $ find valid [1..] 34 | where 35 | valid n = (replicate digits '0') == 36 | (take digits $ hashString $ stuff ++ show n) 37 | 38 | solna :: String -> IO () 39 | solna stuff = do 40 | print $ solve stuff 5 41 | 42 | solnb :: String -> IO () 43 | solnb stuff = do 44 | print $ solve stuff 6 45 | 46 | main :: IO () 47 | main = makeMain solna solnb 48 | -------------------------------------------------------------------------------- /5/Makefile: -------------------------------------------------------------------------------- 1 | ../Soln/Makefile.template -------------------------------------------------------------------------------- /5/README.md: -------------------------------------------------------------------------------- 1 | The trick here is to treat the input passwords as 2 | overlapping groups of letters, and look for groups with the 3 | right properties. 4 | 5 | The Haskell code uses `tile` from the solution library 6 | (`Soln`) to make the necessary groupings. 7 | -------------------------------------------------------------------------------- /5/problem-a.md: -------------------------------------------------------------------------------- 1 | Santa needs help figuring out which strings in his text file 2 | are naughty or nice. 3 | 4 | A nice string is one with all of the following properties: 5 | 6 | * It contains at least three vowels (`aeiou` only), like 7 | `aei`, `xazegov`, or `aeiouaeiouaeiou`. 8 | 9 | * It contains at least one letter that appears twice in a 10 | row, like `xx`, `abcdde` (`dd`), or `aabbccdd` (`aa`, 11 | `bb`, `cc`, or `dd`). 12 | 13 | * It does not contain the strings `ab`, `cd`, `pq`, or `xy`, 14 | even if they are part of one of the other requirements. 15 | 16 | For example: 17 | 18 | * `ugknbfddgicrmopn` is nice because it has at least three 19 | vowels (`u`...`i`...`o`...), a double letter (...`dd`...), 20 | and none of the disallowed substrings. 21 | 22 | * `aaa` is nice because it has at least three vowels and a 23 | double letter, even though the letters used by different 24 | rules overlap. 25 | 26 | * `jchzalrnumimnmhp` is naughty because it has no double letter. 27 | 28 | * `haegwjzuvuyypxyu` is naughty because it contains the string `xy`. 29 | 30 | * `dvszwmarrgswjxmb` is naughty because it contains only one vowel. 31 | 32 | How many strings are nice? 33 | -------------------------------------------------------------------------------- /5/problem-b.md: -------------------------------------------------------------------------------- 1 | Realizing the error of his ways, Santa has switched to a 2 | better model of determining whether a string is naughty or 3 | nice. None of the old rules apply, as they are all clearly 4 | ridiculous. 5 | 6 | Now, a nice string is one with all of the following 7 | properties: 8 | 9 | * It contains a pair of any two letters that appears at 10 | least twice in the string without overlapping, like `xyxy` 11 | (`xy`) or `aabcdefgaa` (`aa`), but not like `aaa` (`aa`, 12 | but it overlaps). 13 | 14 | * It contains at least one letter which repeats with exactly 15 | one letter between them, like `xyx`, `abcdefeghi` (`efe`), 16 | or even `aaa`. 17 | 18 | For example: 19 | 20 | * `qjhvhtzxzqqjkmpb` is nice because is has a pair that 21 | appears twice (`qj`) and a letter that repeats with 22 | exactly one letter between them (`zxz`). 23 | 24 | * `xxyxx` is nice because it has a pair that appears twice 25 | and a letter that repeats with one between, even though 26 | the letters used by each rule overlap. 27 | 28 | * `uurcxstgmygtbstg` is naughty because it has a pair (`tg`) 29 | but no repeat with a single letter between them. 30 | 31 | * `ieodomkazucvgmuy` is naughty because it has a repeating 32 | letter with one between (`odo`), but no pair that appears 33 | twice. 34 | 35 | How many strings are nice under these new rules? 36 | 37 | -------------------------------------------------------------------------------- /5/soln.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | 7 | import Soln 8 | 9 | -- | Check that all niceness properties hold. 10 | nice :: String -> Bool 11 | nice s = 12 | threeVowels && doubledLetter && noMagic 13 | where 14 | threeVowels = 3 <= length (filter (`elem` "aeiou") s) 15 | doubledLetter = any ((>= 2) . length) $ group s 16 | noMagic = not $ any (`elem` ["ab", "cd", "pq", "xy"]) $ tiles 2 1 s 17 | 18 | 19 | solve :: (String -> Bool) -> String -> Int 20 | solve f stuff = length $ filter f $ lines stuff 21 | 22 | solna :: String -> IO () 23 | solna stuff = print $ solve nice stuff 24 | 25 | -- | Check that all nicerness properties hold. The use of 26 | -- 'shortenThrees' turns repeats of length exactly 3 into 27 | -- repeats of length 2 to avoid false positives. 28 | nicer :: String -> Bool 29 | nicer s = 30 | matchingPairs && xyxs 31 | where 32 | matchingPairs = 33 | any ((>= 2) . length) $ group $ sort $ tiles 2 1 $ 34 | concatMap shortenThrees $ group s 35 | where 36 | shortenThrees [x, _ , _] = [x, x] 37 | shortenThrees xs = xs 38 | xyxs = 39 | any xyx $ tiles 3 1 s 40 | where 41 | xyx [c1, _, c3] = c1 == c3 42 | xyx _ = error "bad tile length" 43 | 44 | solnb :: String -> IO () 45 | solnb stuff = print $ solve nicer stuff 46 | 47 | main :: IO () 48 | main = makeMain solna solnb 49 | -------------------------------------------------------------------------------- /6/Makefile: -------------------------------------------------------------------------------- 1 | ../Soln/Makefile.template -------------------------------------------------------------------------------- /6/README.md: -------------------------------------------------------------------------------- 1 | This problem is mostly a command-processing exercise. 2 | 3 | Haskell has reasonably efficient "mutable" arrays, albeit 4 | with confusing syntax and semantics. This solution makes use 5 | of them. 6 | 7 | The split of interpretation and form makes good use of 8 | functional programming tools: the result is code that could 9 | be reused for a variety of similar problems. 10 | -------------------------------------------------------------------------------- /6/problem-a.md: -------------------------------------------------------------------------------- 1 | Because your neighbors keep defeating you in the holiday 2 | house decorating contest year after year, you've decided to 3 | deploy one million lights in a 1000x1000 grid. 4 | 5 | Furthermore, because you've been especially nice this year, 6 | Santa has mailed you instructions on how to display the 7 | ideal lighting configuration. 8 | 9 | Lights in your grid are numbered from 0 to 999 in each 10 | direction; the lights at each corner are at 0,0, 0,999, 11 | 999,999, and 999,0. The instructions include whether to turn 12 | on, turn off, or toggle various inclusive ranges given as 13 | coordinate pairs. Each coordinate pair represents opposite 14 | corners of a rectangle, inclusive; a coordinate pair like 15 | 0,0 through 2,2 therefore refers to 9 lights in a 3x3 16 | square. The lights all start turned off. 17 | 18 | To defeat your neighbors this year, all you have to do is 19 | set up your lights by doing the instructions Santa sent you 20 | in order. 21 | 22 | For example: 23 | 24 | * turn on 0,0 through 999,999 would turn on (or leave on) 25 | every light. 26 | 27 | * toggle 0,0 through 999,0 would toggle the first line of 28 | 1000 lights, turning off the ones that were on, and 29 | turning on the ones that were off. 30 | 31 | * turn off 499,499 through 500,500 would turn off (or leave 32 | off) the middle four lights. 33 | 34 | After following the instructions, how many lights are lit? 35 | -------------------------------------------------------------------------------- /6/problem-b.md: -------------------------------------------------------------------------------- 1 | You just finish implementing your winning light pattern when 2 | you realize you mistranslated Santa's message from Ancient 3 | Nordic Elvish. 4 | 5 | * The light grid you bought actually has individual 6 | brightness controls; each light can have a brightness of 7 | zero or more. The lights all start at zero. 8 | 9 | * The phrase turn on actually means that you should increase 10 | the brightness of those lights by 1. 11 | 12 | * The phrase turn off actually means that you should 13 | decrease the brightness of those lights by 1, to a minimum of zero. 14 | 15 | * The phrase toggle actually means that you should increase 16 | the brightness of those lights by 2. 17 | 18 | What is the total brightness of all lights combined after 19 | following Santa's instructions? 20 | 21 | For example: 22 | 23 | * turn on 0,0 through 0,0 would increase the total 24 | brightness by 1. 25 | 26 | * toggle 0,0 through 999,999 would increase the total 27 | brightness by 2000000. 28 | 29 | After following the instructions, how many lights are lit? 30 | -------------------------------------------------------------------------------- /6/soln.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | {-# LANGUAGE FlexibleContexts #-} 7 | 8 | import Data.Array.IO 9 | 10 | import Soln 11 | 12 | -- | Each dimension goes from 0 to 999 inclusive. 13 | bound :: Int 14 | bound = 999 15 | 16 | -- | Every coordinate in the array, in y-major order. 17 | allCoords :: [(Int, Int)] 18 | allCoords = [(x, y) | x <- [0..bound], y <- [0..bound]] 19 | 20 | -- | A mutable unboxed 2D array of lights. 21 | type LightArray a = IOUArray (Int, Int) a 22 | 23 | -- | Take a coordinate string to a coordinate pair. 24 | parseCoords :: String -> (Int, Int) 25 | parseCoords s = 26 | let (c1, c2) = break (== ',') s in 27 | (read c1, read (tail c2)) 28 | 29 | -- | Given a current state, an operator to apply, 30 | -- and a string describing a rectangle, operate 31 | -- on the affected lights. 32 | operateLights :: MArray IOUArray a IO => 33 | LightArray a -> (a -> a) -> [String] -> IO () 34 | operateLights lights op [c1Str, "through", c2Str] = do 35 | let (x1, y1) = parseCoords c1Str 36 | let (x2, y2) = parseCoords c2Str 37 | mapM_ lightOp [(x, y) | x <- [x1 `min` x2..x1 `max` x2], 38 | y <- [y1 `min` y2..y1 `max` y2] ] 39 | where 40 | lightOp coord = do 41 | b <- readArray lights coord 42 | writeArray lights coord (op b) 43 | return () 44 | operateLights _ _ _ = error "bad coords" 45 | 46 | -- | Uninterpreted lighting commands. 47 | data Op = OpOff | OpOn | OpToggle 48 | 49 | -- | English interpretation of lighting commands. 50 | opsEnglish :: Op -> (Bool -> Bool) 51 | opsEnglish OpOff = const False 52 | opsEnglish OpOn = const True 53 | opsEnglish OpToggle = not 54 | 55 | -- | Elvish interpretation of lighting commands. 56 | opsElvish :: Op -> (Int -> Int) 57 | opsElvish OpOff x = 0 `max` (x - 1) 58 | opsElvish OpOn x = x + 1 59 | opsElvish OpToggle x = x + 2 60 | 61 | -- | Given a command string, operate the lights 62 | -- according to the given interpretation. 63 | runCommand :: MArray IOUArray a IO => 64 | LightArray a -> (Op -> (a -> a)) -> [String] -> IO () 65 | runCommand lights ops ("turn" : "off" : coords) = 66 | operateLights lights (ops OpOff) coords 67 | runCommand lights ops ("turn" : "on" : coords) = 68 | operateLights lights (ops OpOn) coords 69 | runCommand lights ops ("toggle" : coords) = 70 | operateLights lights (ops OpToggle) coords 71 | runCommand _ _ _ = error "bad command" 72 | 73 | -- | Strategy: Initialize the array as indicated, then run all 74 | -- the commands according to the given interpretation and 75 | -- process the resulting array elements as indicated 76 | soln :: MArray IOUArray a IO => 77 | String -> a -> (Op -> a -> a) -> ([a] -> Int) -> IO () 78 | soln stuff initialValue interp summarize = do 79 | lights <- newArray ((0, 0), (bound, bound)) initialValue 80 | mapM_ (runCommand lights interp) $ map words $ lines stuff 81 | ts <- mapM (readArray lights) allCoords 82 | print $ summarize ts 83 | 84 | solna :: String -> IO () 85 | solna stuff = 86 | soln stuff False opsEnglish (length . filter id) 87 | 88 | solnb :: String -> IO () 89 | solnb stuff = 90 | soln stuff 0 opsElvish sum 91 | 92 | main :: IO () 93 | main = makeMain solna solnb 94 | -------------------------------------------------------------------------------- /7/Makefile: -------------------------------------------------------------------------------- 1 | ../Soln/Makefile.template -------------------------------------------------------------------------------- /7/README.md: -------------------------------------------------------------------------------- 1 | This problem requires parsing and evaluation of a 2 | *combinatorial* circuit. Because there is no feedback and no 3 | nondeterminism, the evaluation can be memoized for 4 | performance, and no infinite recursion is possible. 5 | 6 | I chose to memoize by simply modifying the circuit. One 7 | could also use a memo monad, or just use the state monad 8 | to remember the modified circuit, but it seemed easier 9 | to just return the new circuit with the evaluation. There 10 | is mutual recursion here. There are also some interesting 11 | type issues: having the circuit map just have bare names 12 | on the left is a bit dodgy, but the obvious alternative 13 | involves gratuitous typeclasses. 14 | -------------------------------------------------------------------------------- /7/input.txt: -------------------------------------------------------------------------------- 1 | bn RSHIFT 2 -> bo 2 | lf RSHIFT 1 -> ly 3 | fo RSHIFT 3 -> fq 4 | cj OR cp -> cq 5 | fo OR fz -> ga 6 | t OR s -> u 7 | lx -> a 8 | NOT ax -> ay 9 | he RSHIFT 2 -> hf 10 | lf OR lq -> lr 11 | lr AND lt -> lu 12 | dy OR ej -> ek 13 | 1 AND cx -> cy 14 | hb LSHIFT 1 -> hv 15 | 1 AND bh -> bi 16 | ih AND ij -> ik 17 | c LSHIFT 1 -> t 18 | ea AND eb -> ed 19 | km OR kn -> ko 20 | NOT bw -> bx 21 | ci OR ct -> cu 22 | NOT p -> q 23 | lw OR lv -> lx 24 | NOT lo -> lp 25 | fp OR fv -> fw 26 | o AND q -> r 27 | dh AND dj -> dk 28 | ap LSHIFT 1 -> bj 29 | bk LSHIFT 1 -> ce 30 | NOT ii -> ij 31 | gh OR gi -> gj 32 | kk RSHIFT 1 -> ld 33 | lc LSHIFT 1 -> lw 34 | lb OR la -> lc 35 | 1 AND am -> an 36 | gn AND gp -> gq 37 | lf RSHIFT 3 -> lh 38 | e OR f -> g 39 | lg AND lm -> lo 40 | ci RSHIFT 1 -> db 41 | cf LSHIFT 1 -> cz 42 | bn RSHIFT 1 -> cg 43 | et AND fe -> fg 44 | is OR it -> iu 45 | kw AND ky -> kz 46 | ck AND cl -> cn 47 | bj OR bi -> bk 48 | gj RSHIFT 1 -> hc 49 | iu AND jf -> jh 50 | NOT bs -> bt 51 | kk OR kv -> kw 52 | ks AND ku -> kv 53 | hz OR ik -> il 54 | b RSHIFT 1 -> v 55 | iu RSHIFT 1 -> jn 56 | fo RSHIFT 5 -> fr 57 | be AND bg -> bh 58 | ga AND gc -> gd 59 | hf OR hl -> hm 60 | ld OR le -> lf 61 | as RSHIFT 5 -> av 62 | fm OR fn -> fo 63 | hm AND ho -> hp 64 | lg OR lm -> ln 65 | NOT kx -> ky 66 | kk RSHIFT 3 -> km 67 | ek AND em -> en 68 | NOT ft -> fu 69 | NOT jh -> ji 70 | jn OR jo -> jp 71 | gj AND gu -> gw 72 | d AND j -> l 73 | et RSHIFT 1 -> fm 74 | jq OR jw -> jx 75 | ep OR eo -> eq 76 | lv LSHIFT 15 -> lz 77 | NOT ey -> ez 78 | jp RSHIFT 2 -> jq 79 | eg AND ei -> ej 80 | NOT dm -> dn 81 | jp AND ka -> kc 82 | as AND bd -> bf 83 | fk OR fj -> fl 84 | dw OR dx -> dy 85 | lj AND ll -> lm 86 | ec AND ee -> ef 87 | fq AND fr -> ft 88 | NOT kp -> kq 89 | ki OR kj -> kk 90 | cz OR cy -> da 91 | as RSHIFT 3 -> au 92 | an LSHIFT 15 -> ar 93 | fj LSHIFT 15 -> fn 94 | 1 AND fi -> fj 95 | he RSHIFT 1 -> hx 96 | lf RSHIFT 2 -> lg 97 | kf LSHIFT 15 -> kj 98 | dz AND ef -> eh 99 | ib OR ic -> id 100 | lf RSHIFT 5 -> li 101 | bp OR bq -> br 102 | NOT gs -> gt 103 | fo RSHIFT 1 -> gh 104 | bz AND cb -> cc 105 | ea OR eb -> ec 106 | lf AND lq -> ls 107 | NOT l -> m 108 | hz RSHIFT 3 -> ib 109 | NOT di -> dj 110 | NOT lk -> ll 111 | jp RSHIFT 3 -> jr 112 | jp RSHIFT 5 -> js 113 | NOT bf -> bg 114 | s LSHIFT 15 -> w 115 | eq LSHIFT 1 -> fk 116 | jl OR jk -> jm 117 | hz AND ik -> im 118 | dz OR ef -> eg 119 | 1 AND gy -> gz 120 | la LSHIFT 15 -> le 121 | br AND bt -> bu 122 | NOT cn -> co 123 | v OR w -> x 124 | d OR j -> k 125 | 1 AND gd -> ge 126 | ia OR ig -> ih 127 | NOT go -> gp 128 | NOT ed -> ee 129 | jq AND jw -> jy 130 | et OR fe -> ff 131 | aw AND ay -> az 132 | ff AND fh -> fi 133 | ir LSHIFT 1 -> jl 134 | gg LSHIFT 1 -> ha 135 | x RSHIFT 2 -> y 136 | db OR dc -> dd 137 | bl OR bm -> bn 138 | ib AND ic -> ie 139 | x RSHIFT 3 -> z 140 | lh AND li -> lk 141 | ce OR cd -> cf 142 | NOT bb -> bc 143 | hi AND hk -> hl 144 | NOT gb -> gc 145 | 1 AND r -> s 146 | fw AND fy -> fz 147 | fb AND fd -> fe 148 | 1 AND en -> eo 149 | z OR aa -> ab 150 | bi LSHIFT 15 -> bm 151 | hg OR hh -> hi 152 | kh LSHIFT 1 -> lb 153 | cg OR ch -> ci 154 | 1 AND kz -> la 155 | gf OR ge -> gg 156 | gj RSHIFT 2 -> gk 157 | dd RSHIFT 2 -> de 158 | NOT ls -> lt 159 | lh OR li -> lj 160 | jr OR js -> jt 161 | au AND av -> ax 162 | 0 -> c 163 | he AND hp -> hr 164 | id AND if -> ig 165 | et RSHIFT 5 -> ew 166 | bp AND bq -> bs 167 | e AND f -> h 168 | ly OR lz -> ma 169 | 1 AND lu -> lv 170 | NOT jd -> je 171 | ha OR gz -> hb 172 | dy RSHIFT 1 -> er 173 | iu RSHIFT 2 -> iv 174 | NOT hr -> hs 175 | as RSHIFT 1 -> bl 176 | kk RSHIFT 2 -> kl 177 | b AND n -> p 178 | ln AND lp -> lq 179 | cj AND cp -> cr 180 | dl AND dn -> do 181 | ci RSHIFT 2 -> cj 182 | as OR bd -> be 183 | ge LSHIFT 15 -> gi 184 | hz RSHIFT 5 -> ic 185 | dv LSHIFT 1 -> ep 186 | kl OR kr -> ks 187 | gj OR gu -> gv 188 | he RSHIFT 5 -> hh 189 | NOT fg -> fh 190 | hg AND hh -> hj 191 | b OR n -> o 192 | jk LSHIFT 15 -> jo 193 | gz LSHIFT 15 -> hd 194 | cy LSHIFT 15 -> dc 195 | kk RSHIFT 5 -> kn 196 | ci RSHIFT 3 -> ck 197 | at OR az -> ba 198 | iu RSHIFT 3 -> iw 199 | ko AND kq -> kr 200 | NOT eh -> ei 201 | aq OR ar -> as 202 | iy AND ja -> jb 203 | dd RSHIFT 3 -> df 204 | bn RSHIFT 3 -> bp 205 | 1 AND cc -> cd 206 | at AND az -> bb 207 | x OR ai -> aj 208 | kk AND kv -> kx 209 | ao OR an -> ap 210 | dy RSHIFT 3 -> ea 211 | x RSHIFT 1 -> aq 212 | eu AND fa -> fc 213 | kl AND kr -> kt 214 | ia AND ig -> ii 215 | df AND dg -> di 216 | NOT fx -> fy 217 | k AND m -> n 218 | bn RSHIFT 5 -> bq 219 | km AND kn -> kp 220 | dt LSHIFT 15 -> dx 221 | hz RSHIFT 2 -> ia 222 | aj AND al -> am 223 | cd LSHIFT 15 -> ch 224 | hc OR hd -> he 225 | he RSHIFT 3 -> hg 226 | bn OR by -> bz 227 | NOT kt -> ku 228 | z AND aa -> ac 229 | NOT ak -> al 230 | cu AND cw -> cx 231 | NOT ie -> if 232 | dy RSHIFT 2 -> dz 233 | ip LSHIFT 15 -> it 234 | de OR dk -> dl 235 | au OR av -> aw 236 | jg AND ji -> jj 237 | ci AND ct -> cv 238 | dy RSHIFT 5 -> eb 239 | hx OR hy -> hz 240 | eu OR fa -> fb 241 | gj RSHIFT 3 -> gl 242 | fo AND fz -> gb 243 | 1 AND jj -> jk 244 | jp OR ka -> kb 245 | de AND dk -> dm 246 | ex AND ez -> fa 247 | df OR dg -> dh 248 | iv OR jb -> jc 249 | x RSHIFT 5 -> aa 250 | NOT hj -> hk 251 | NOT im -> in 252 | fl LSHIFT 1 -> gf 253 | hu LSHIFT 15 -> hy 254 | iq OR ip -> ir 255 | iu RSHIFT 5 -> ix 256 | NOT fc -> fd 257 | NOT el -> em 258 | ck OR cl -> cm 259 | et RSHIFT 3 -> ev 260 | hw LSHIFT 1 -> iq 261 | ci RSHIFT 5 -> cl 262 | iv AND jb -> jd 263 | dd RSHIFT 5 -> dg 264 | as RSHIFT 2 -> at 265 | NOT jy -> jz 266 | af AND ah -> ai 267 | 1 AND ds -> dt 268 | jx AND jz -> ka 269 | da LSHIFT 1 -> du 270 | fs AND fu -> fv 271 | jp RSHIFT 1 -> ki 272 | iw AND ix -> iz 273 | iw OR ix -> iy 274 | eo LSHIFT 15 -> es 275 | ev AND ew -> ey 276 | ba AND bc -> bd 277 | fp AND fv -> fx 278 | jc AND je -> jf 279 | et RSHIFT 2 -> eu 280 | kg OR kf -> kh 281 | iu OR jf -> jg 282 | er OR es -> et 283 | fo RSHIFT 2 -> fp 284 | NOT ca -> cb 285 | bv AND bx -> by 286 | u LSHIFT 1 -> ao 287 | cm AND co -> cp 288 | y OR ae -> af 289 | bn AND by -> ca 290 | 1 AND ke -> kf 291 | jt AND jv -> jw 292 | fq OR fr -> fs 293 | dy AND ej -> el 294 | NOT kc -> kd 295 | ev OR ew -> ex 296 | dd OR do -> dp 297 | NOT cv -> cw 298 | gr AND gt -> gu 299 | dd RSHIFT 1 -> dw 300 | NOT gw -> gx 301 | NOT iz -> ja 302 | 1 AND io -> ip 303 | NOT ag -> ah 304 | b RSHIFT 5 -> f 305 | NOT cr -> cs 306 | kb AND kd -> ke 307 | jr AND js -> ju 308 | cq AND cs -> ct 309 | il AND in -> io 310 | NOT ju -> jv 311 | du OR dt -> dv 312 | dd AND do -> dq 313 | b RSHIFT 2 -> d 314 | jm LSHIFT 1 -> kg 315 | NOT dq -> dr 316 | bo OR bu -> bv 317 | gk OR gq -> gr 318 | he OR hp -> hq 319 | NOT h -> i 320 | hf AND hl -> hn 321 | gv AND gx -> gy 322 | x AND ai -> ak 323 | bo AND bu -> bw 324 | hq AND hs -> ht 325 | hz RSHIFT 1 -> is 326 | gj RSHIFT 5 -> gm 327 | g AND i -> j 328 | gk AND gq -> gs 329 | dp AND dr -> ds 330 | b RSHIFT 3 -> e 331 | gl AND gm -> go 332 | gl OR gm -> gn 333 | y AND ae -> ag 334 | hv OR hu -> hw 335 | 1674 -> b 336 | ab AND ad -> ae 337 | NOT ac -> ad 338 | 1 AND ht -> hu 339 | NOT hn -> ho 340 | -------------------------------------------------------------------------------- /7/problem-a.md: -------------------------------------------------------------------------------- 1 | This year, Santa brought little Bobby Tables a set of wires 2 | and bitwise logic gates! Unfortunately, little Bobby is a 3 | little under the recommended age range, and he needs help 4 | assembling the circuit. 5 | 6 | Each wire has an identifier (some lowercase letters) and can 7 | carry a 16-bit signal (a number from 0 to 65535). A signal 8 | is provided to each wire by a gate, another wire, or some 9 | specific value. Each wire can only get a signal from one 10 | source, but can provide its signal to multiple 11 | destinations. A gate provides no signal until all of its 12 | inputs have a signal. 13 | 14 | The included instructions booklet describes how to connect 15 | the parts together: `x AND y -> z` means to connect wires x 16 | and y to an AND gate, and then connect its output to wire z. 17 | 18 | For example: 19 | 20 | * `123 -> x` means that the signal 123 is provided to wire x. 21 | * `x AND y -> z` means that the bitwise AND of wire x and 22 | wire y is provided to wire z. 23 | * `p LSHIFT 2 -> q` means that the value from wire p is 24 | left-shifted by 2 and then provided to wire q. 25 | * `NOT e -> f` means that the bitwise complement of the 26 | value from wire e is provided to wire f. 27 | 28 | Other possible gates include OR (bitwise OR) and RSHIFT 29 | (right-shift). If, for some reason, you'd like to emulate 30 | the circuit instead, almost all programming languages (for 31 | example, C, JavaScript, or Python) provide operators for 32 | these gates. 33 | 34 | For example, here is a simple circuit: 35 | 36 | 123 -> x 37 | 456 -> y 38 | x AND y -> d 39 | x OR y -> e 40 | x LSHIFT 2 -> f 41 | y RSHIFT 2 -> g 42 | NOT x -> h 43 | NOT y -> i 44 | 45 | After it is run, these are the signals on the wires: 46 | 47 | d: 72 48 | e: 507 49 | f: 492 50 | g: 114 51 | h: 65412 52 | i: 65079 53 | x: 123 54 | y: 456 55 | 56 | In little Bobby's kit's instructions booklet (provided as 57 | your puzzle input), what signal is ultimately provided to 58 | wire a? 59 | -------------------------------------------------------------------------------- /7/problem-b.md: -------------------------------------------------------------------------------- 1 | Now, take the signal you got on wire a, override wire b to 2 | that signal, and reset the other wires (including wire 3 | a). What new signal is ultimately provided to wire a? 4 | -------------------------------------------------------------------------------- /7/soln.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | 7 | import qualified Data.Map.Strict as M 8 | 9 | import Soln 10 | 11 | -- | Value or variable. 12 | data Term = TermConst Word16 13 | | TermVar String 14 | 15 | -- | Gate calculation. 16 | data Gate = GateConst Word16 17 | | GateUnary (Word16 -> Word16) Term 18 | | GateBinary (Word16 -> Word16 -> Word16) Term Term 19 | 20 | -- | List of current mappings of variables to calculations. 21 | type GateMap = M.Map String Gate 22 | 23 | -- | Parse a single term. 24 | parseTerm :: String -> Term 25 | parseTerm t@(t1 : _) 26 | | isDigit t1 = TermConst (read t) 27 | | isAlpha t1 = TermVar t 28 | | otherwise = error "malformed term" 29 | parseTerm _ = error "empty term" 30 | 31 | -- | 'fromIntegral' the RHS, because Haskell has no promotions 32 | -- and 'shiftL' and 'shiftR' require 'Int' RHS for some reason. 33 | fir :: (Word16 -> Int -> Word16) -> (Word16 -> Word16 -> Word16) 34 | fir f a1 a2 = f a1 (fromIntegral a2) 35 | 36 | -- | Parse a single gate. 37 | parseGate :: [String] -> (String, Gate) 38 | parseGate [a, "->", r] = 39 | (r, GateUnary id (parseTerm a)) 40 | parseGate ["NOT", a, "->", r] = 41 | (r, GateUnary complement (parseTerm a)) 42 | parseGate [a1, "LSHIFT", a2, "->", r] = 43 | (r, GateBinary (fir shiftL) (parseTerm a1) (parseTerm a2)) 44 | parseGate [a1, "RSHIFT", a2, "->", r] = 45 | (r, GateBinary (fir shiftR) (parseTerm a1) (parseTerm a2)) 46 | parseGate [a1, "AND", a2, "->", r] = 47 | (r, GateBinary (.&.) (parseTerm a1) (parseTerm a2)) 48 | parseGate [a1, "OR", a2, "->", r] = 49 | (r, GateBinary (.|.) (parseTerm a1) (parseTerm a2)) 50 | parseGate _ = error "bad gate" 51 | 52 | -- | Parse the list of gates. 53 | parseGates :: String -> GateMap 54 | parseGates stuff = 55 | M.fromList $ map parseGate $ map words $ lines stuff 56 | 57 | -- | Evaluate a single 'Term' in the context of 58 | -- a 'GateMap', returning a new 'GateMap' with 59 | -- the term substituted for its value, together 60 | -- with the value. 61 | evalTerm :: GateMap -> Term -> (GateMap, Word16) 62 | evalTerm circuit (TermConst n) = 63 | (circuit, n) 64 | evalTerm circuit (TermVar a) = 65 | let (circuit', n) = eval circuit a in 66 | (M.insert a (GateConst n) circuit', n) 67 | 68 | -- | Evaluate a variable in the context of 69 | -- a 'GateMap', returning the result of the 70 | -- evaluation plus a new 'GateMap' with 71 | -- all substitutions made. 72 | eval :: GateMap -> String -> (GateMap, Word16) 73 | eval circuit target = 74 | case M.lookup target circuit of 75 | Just (GateConst n) -> 76 | (circuit, n) 77 | Just (GateUnary op a) -> 78 | let (circuit', n) = evalTerm circuit a in 79 | (circuit', op n) 80 | Just (GateBinary op a1 a2) -> 81 | let (circuit', n1) = evalTerm circuit a1 in 82 | let (circuit'', n2) = evalTerm circuit' a2 in 83 | (circuit'', op n1 n2) 84 | Nothing -> error "bad target" 85 | 86 | -- | Parse the gates, solve for "a", print the answer. 87 | solna :: String -> IO () 88 | solna stuff = do 89 | let circuit = parseGates stuff 90 | print $ snd $ eval circuit "a" 91 | 92 | -- | Strategy: Evaluate for "a" in the original circuit, 93 | -- substitute the resulting value for "b" in the original 94 | -- circuit, evaluate for "b". 95 | solnb :: String -> IO () 96 | solnb stuff = do 97 | let circuit = parseGates stuff 98 | let n = snd $ eval circuit "a" 99 | let circuit' = M.insert "b" (GateConst n) circuit 100 | print $ snd $ eval circuit' "a" 101 | 102 | main :: IO () 103 | main = makeMain solna solnb 104 | -------------------------------------------------------------------------------- /7/topo.txt: -------------------------------------------------------------------------------- 1 | bn bo 2 | lf ly 3 | fo fq 4 | cj cp cq 5 | fo fz ga 6 | t s u 7 | lx a 8 | ax ay 9 | he hf 10 | lf lq lr 11 | lr lt lu 12 | dy ej ek 13 | cx cy 14 | hb hv 15 | bh bi 16 | ih ij ik 17 | c t 18 | ea eb ed 19 | km kn ko 20 | bw bx 21 | ci ct cu 22 | p q 23 | lw lv lx 24 | lo lp 25 | fp fv fw 26 | o q r 27 | dh dj dk 28 | ap bj 29 | bk ce 30 | ii ij 31 | gh gi gj 32 | kk ld 33 | lc lw 34 | lb la lc 35 | am an 36 | gn gp gq 37 | lf lh 38 | e f g 39 | lg lm lo 40 | ci db 41 | cf cz 42 | bn cg 43 | et fe fg 44 | is it iu 45 | kw ky kz 46 | ck cl cn 47 | bj bi bk 48 | gj hc 49 | iu jf jh 50 | bs bt 51 | kk kv kw 52 | ks ku kv 53 | hz ik il 54 | b v 55 | iu jn 56 | fo fr 57 | be bg bh 58 | ga gc gd 59 | hf hl hm 60 | ld le lf 61 | as av 62 | fm fn fo 63 | hm ho hp 64 | lg lm ln 65 | kx ky 66 | kk km 67 | ek em en 68 | ft fu 69 | jh ji 70 | jn jo jp 71 | gj gu gw 72 | d j l 73 | et fm 74 | jq jw jx 75 | ep eo eq 76 | lv lz 77 | ey ez 78 | jp jq 79 | eg ei ej 80 | dm dn 81 | jp ka kc 82 | as bd bf 83 | fk fj fl 84 | dw dx dy 85 | lj ll lm 86 | ec ee ef 87 | fq fr ft 88 | kp kq 89 | ki kj kk 90 | cz cy da 91 | as au 92 | an ar 93 | fj fn 94 | fi fj 95 | he hx 96 | lf lg 97 | kf kj 98 | dz ef eh 99 | ib ic id 100 | lf li 101 | bp bq br 102 | gs gt 103 | fo gh 104 | bz cb cc 105 | ea eb ec 106 | lf lq ls 107 | l m 108 | hz ib 109 | di dj 110 | lk ll 111 | jp jr 112 | jp js 113 | bf bg 114 | s w 115 | eq fk 116 | jl jk jm 117 | hz ik im 118 | dz ef eg 119 | gy gz 120 | la le 121 | br bt bu 122 | cn co 123 | v w x 124 | d j k 125 | gd ge 126 | ia ig ih 127 | go gp 128 | ed ee 129 | jq jw jy 130 | et fe ff 131 | aw ay az 132 | ff fh fi 133 | ir jl 134 | gg ha 135 | x y 136 | db dc dd 137 | bl bm bn 138 | ib ic ie 139 | x z 140 | lh li lk 141 | ce cd cf 142 | bb bc 143 | hi hk hl 144 | gb gc 145 | r s 146 | fw fy fz 147 | fb fd fe 148 | en eo 149 | z aa ab 150 | bi bm 151 | hg hh hi 152 | kh lb 153 | cg ch ci 154 | kz la 155 | gf ge gg 156 | gj gk 157 | dd de 158 | ls lt 159 | lh li lj 160 | jr js jt 161 | au av ax 162 | he hp hr 163 | id if ig 164 | et ew 165 | bp bq bs 166 | e f h 167 | ly lz ma 168 | lu lv 169 | jd je 170 | ha gz hb 171 | dy er 172 | iu iv 173 | hr hs 174 | as bl 175 | kk kl 176 | b n p 177 | ln lp lq 178 | cj cp cr 179 | dl dn do 180 | ci cj 181 | as bd be 182 | ge gi 183 | hz ic 184 | dv ep 185 | kl kr ks 186 | gj gu gv 187 | he hh 188 | fg fh 189 | hg hh hj 190 | b n o 191 | jk jo 192 | gz hd 193 | cy dc 194 | kk kn 195 | ci ck 196 | at az ba 197 | iu iw 198 | ko kq kr 199 | eh ei 200 | aq ar as 201 | iy ja jb 202 | dd df 203 | bn bp 204 | cc cd 205 | at az bb 206 | x ai aj 207 | kk kv kx 208 | ao an ap 209 | dy ea 210 | x aq 211 | eu fa fc 212 | kl kr kt 213 | ia ig ii 214 | df dg di 215 | fx fy 216 | k m n 217 | bn bq 218 | km kn kp 219 | dt dx 220 | hz ia 221 | aj al am 222 | cd ch 223 | hc hd he 224 | he hg 225 | bn by bz 226 | kt ku 227 | z aa ac 228 | ak al 229 | cu cw cx 230 | ie if 231 | dy dz 232 | ip it 233 | de dk dl 234 | au av aw 235 | jg ji jj 236 | ci ct cv 237 | dy eb 238 | hx hy hz 239 | eu fa fb 240 | gj gl 241 | fo fz gb 242 | jj jk 243 | jp ka kb 244 | de dk dm 245 | ex ez fa 246 | df dg dh 247 | iv jb jc 248 | x aa 249 | hj hk 250 | im in 251 | fl gf 252 | hu hy 253 | iq ip ir 254 | iu ix 255 | fc fd 256 | el em 257 | ck cl cm 258 | et ev 259 | hw iq 260 | ci cl 261 | iv jb jd 262 | dd dg 263 | as at 264 | jy jz 265 | af ah ai 266 | ds dt 267 | jx jz ka 268 | da du 269 | fs fu fv 270 | jp ki 271 | iw ix iz 272 | iw ix iy 273 | eo es 274 | ev ew ey 275 | ba bc bd 276 | fp fv fx 277 | jc je jf 278 | et eu 279 | kg kf kh 280 | iu jf jg 281 | er es et 282 | fo fp 283 | ca cb 284 | bv bx by 285 | u ao 286 | cm co cp 287 | y ae af 288 | bn by ca 289 | ke kf 290 | jt jv jw 291 | fq fr fs 292 | dy ej el 293 | kc kd 294 | ev ew ex 295 | dd do dp 296 | cv cw 297 | gr gt gu 298 | dd dw 299 | gw gx 300 | iz ja 301 | io ip 302 | ag ah 303 | b f 304 | cr cs 305 | kb kd ke 306 | jr js ju 307 | cq cs ct 308 | il in io 309 | ju jv 310 | du dt dv 311 | dd do dq 312 | b d 313 | jm kg 314 | dq dr 315 | bo bu bv 316 | gk gq gr 317 | he hp hq 318 | h i 319 | hf hl hn 320 | gv gx gy 321 | x ai ak 322 | bo bu bw 323 | hq hs ht 324 | hz is 325 | gj gm 326 | g i j 327 | gk gq gs 328 | dp dr ds 329 | b e 330 | gl gm go 331 | gl gm gn 332 | y ae ag 333 | hv hu hw 334 | ab ad ae 335 | ac ad 336 | ht hu 337 | hn ho 338 | -------------------------------------------------------------------------------- /8/Makefile: -------------------------------------------------------------------------------- 1 | ../Soln/Makefile.template -------------------------------------------------------------------------------- /8/README.md: -------------------------------------------------------------------------------- 1 | This is just a list-processing exercise. It is probably 2 | better to just count characters rather than 3 | unescaping/escaping strings and taking lengths. One could 4 | use the built-in Haskell `read` and `show`, but this is 5 | fraught with error potential. 6 | 7 | The only interesting Haskell here is treating strings as 8 | lists, and the use of the `envelop` fold from `Soln`. 9 | -------------------------------------------------------------------------------- /8/input.txt: -------------------------------------------------------------------------------- 1 | "\xa8br\x8bjr\"" 2 | "nq" 3 | "zjrfcpbktjmrzgsz\xcaqsc\x03n\"huqab" 4 | "daz\\zyyxddpwk" 5 | "draes\xa2n\\g\x27ek\"lj\"\\viqych" 6 | "nnx\\krnrfomdnt\x2flbl\xd2xpo\"cp\"k" 7 | "kwdaapalq" 8 | "u\"ptk" 9 | "ckhorczuiudfjmmcc\\u\"wozqxibsfjma" 10 | "ydctdrxat\"pd\"lwi\"bjesevfw\xe8" 11 | "v\"\xa8rrzep\"\"r" 12 | "nbydghkfvmq\\\xe0\"lfsrsvlsj\"i\x61liif" 13 | "jsas\"u" 14 | "odipikxlo" 15 | "\"rnubsgwltqkbsu\"pcpcs" 16 | "eitk\\f\\mhcqqoym\\ji" 17 | "vnedc" 18 | "\"lhcaurdqzyjyu" 19 | "haxzsa\"zcn\"y\"foclgtjfcnv\"m\x68krc" 20 | "\"eoeggg\"tmiydvcay\"vfavc" 21 | "snqvyqoncwxcvwbdktoywch" 22 | "rnfgjsyr\xd5wacy" 23 | "ik\"hebrpvsts" 24 | "txw" 25 | "\x15pxtdkogd\"urbm\"gevhh\"nxr\x3erxtk" 26 | "cetqtcy" 27 | "inleep\\mgl" 28 | "uflwbxvww\x2cxzezqnaply\"yh\"qlllzk" 29 | "eepak\"xqtedzt" 30 | "na\x61qzfieafvyrsnwkssznohjmc" 31 | "yceaonylz\xc1\\jrlbbkzwsidfi" 32 | "ybqafngkcqpbp" 33 | "\xaft" 34 | "yidjpaobqydso" 35 | "ju\\ldxig\\lrdrhjcmm\x77rc" 36 | "tylacqeslnwj\x48ds\"tjxa" 37 | "efbfm" 38 | "\\fxkgoprgdcjgyajykg\\dtbrz" 39 | "eujvva" 40 | "h\x7acwfpikme\\vwthyvrqdnx\"" 41 | "rbpbrxm\\\"\"\"voxx" 42 | "ykiw\"tkb\\lforu\"rsf\\tf\"x\"rqti" 43 | "e\\wh\x77aqeugiq\\ihhfqfuaij" 44 | "g\"t\\o" 45 | "nxzo\"hf\\xp" 46 | "dxiaqfo\xea" 47 | "kali\\zczhiqkqzybjj\"fgdjnik" 48 | "zdkgrqmdv" 49 | "bimxim\xb6lrwsaj\"ui\"a" 50 | "\"rrznitibgx\\olpsjmjqzctxaubdifsq" 51 | "zb\"khzixaacmhuzmlymoformipdzml" 52 | "qfwi" 53 | "hjwsxfpphttjy\"\"zixais\xbblgnqfto" 54 | "puj\\qmyu\"nqgaqfthbwjokbmrpbhpi" 55 | "cyxdpkh\\\"" 56 | "q" 57 | "m" 58 | "tbxdzzllarlo" 59 | "gbtys" 60 | "gytilk\\vlqxvcuutjunrqc" 61 | "uugkvcuzan\\eyhb" 62 | "yaxr\"genlbgw\"\\uc" 63 | "nrgecjeip\\sjdvgqaqxwsqactopu" 64 | "pu\"r\"txpyrkfny\\zmwfneyvwmnkkdipv" 65 | "jm\xa3bhwvq" 66 | "qxojmnml\"w\x9airr" 67 | "xbzsuihs\x4dcedy\xaclrhgii\\\"" 68 | "drgjirusrekrwmvxllwdm" 69 | "\x28hfxnfpycmpnkku\"csuf\xaarxlqyg\"x" 70 | "\"zvz\\rmg\"\\sxxoifffyqfyn\"iq\"ps" 71 | "\"z" 72 | "zbwkmk\"sgzos\x93gtc\"" 73 | "bvm\x28aa\\\\\"pywuhaniox\\z\\hbp\xd7mold" 74 | "aszgvsyna" 75 | "qf\"vdwuss" 76 | "lnohni\"qwiacjsjegstlbfq\\kyjhyd" 77 | "c\\naawulxlqplnacvytspry\xf5ytxxqq" 78 | "razwqmsqgbaaxcd\\f" 79 | "radggyrjrg\"zx" 80 | "\"pu\x11t\\ajcjuieinlkvya" 81 | "veggiskh" 82 | "eglfhjxiet\"kouqfskwsy\"hpthsldel" 83 | "mv\xc1b\"f\\shrssnjwcpmurepdxdlcj" 84 | "dlayjd\"suvzotgdtc" 85 | "\xa9pvxeopn" 86 | "lpplsaxy\"oiwaq" 87 | "hqwh\\lusv" 88 | "hykykwlx\"\xa5atkgh\\d\x63dff" 89 | "vfktanpjy\"xxetc" 90 | "dnhwkgjnsmsswfuelvihvjl\"jtf" 91 | "x\"dwvzra\"nbbsewftehczgbvfzd\"rau" 92 | "csfi\"mzejnjqkqupwadrgti\"von" 93 | "xckf\xf7xsm\\pgvlpetjndpyblais\\z" 94 | "yecy\x6fuj\x58bwpgeuiw\"mdu" 95 | "fgb" 96 | "c\\lx\x3efthet\xfdelgvwvpem" 97 | "kgyrmarvfwjinlowt" 98 | "yzte" 99 | "vc\"z" 100 | "sxevqfzmmdwsuu\"" 101 | "fxbaercmcy\xb6md" 102 | "f" 103 | "m\x44gqbcppho\\b" 104 | "gtafr\x57m\x11jy\"\"erwmmpiwjkbckuw" 105 | "ufdjt\"kssprzxqixzxmq\x58q" 106 | "yzbyo\"lfdbyaxexyfbnyv\\\xe8xmre" 107 | "u\x43ntr\\\\byyfjr\"iveujvnwsqbnpuvrta" 108 | "us\xf6bai" 109 | "c\\edh" 110 | "tzckolphexfq\\\x23\xfbdqv\\\"m" 111 | "yjafhbvhhj\x1b\"bplb" 112 | "\"o" 113 | "rubahvmp\"" 114 | "qmkukrnrmqumh" 115 | "wdpxyvyidhwjf\\nabbijwhr\xc5bksvy\"p" 116 | "u\"prlpg\"" 117 | "nsvcquyxbwilsxxemf\xd9leq" 118 | "y\xcetxuafl" 119 | "it" 120 | "kwdlysf\\xjpelae" 121 | "viwh\x58wpjjlnvryuti\x2chngrx\\nhtkui" 122 | "vhn\x9ehre\xc3ncsqbozms\"nl" 123 | "ytc\xa3mgeeogjcqavmmmd" 124 | "xzlexlitseozoxtpzzutfq" 125 | "cish\x07lmovj" 126 | "ekbflwqzaiivdr\"pq\\azrfbntqwkn" 127 | "uc\"xdbegmlmhksofzohavtrnxf" 128 | "xfdnrdqdrcjzbe" 129 | "ndg\"ckgrpisib\"rg\"p\\lmpfzlssnvk" 130 | "witfjwpbyyzlop" 131 | "zonlww\"emrbcsgdtrg\"rjzy\x64zqntlw" 132 | "dvgb\"zn\\vrbzema\"ckmd" 133 | "\\vdlmxhlvldk\"pmzazeip" 134 | "\"\"r" 135 | "rsntinv" 136 | "iy" 137 | "lr\x20efh" 138 | "csgexlb\"zqdavlxxhtdbh\"\"\x0fkpvhiphm" 139 | "ouwhp\"ogbft" 140 | "cm\\ckltng\"dw\x8brf\xf0eppgckd" 141 | "zmnlsgalhpkejsizfsbtnfliu\"nhc" 142 | "pnrkaayqvwpdjbhcrbb\"yfeq\"aq" 143 | "ozh\\hoxow\x2csrtr\\r\"" 144 | "bqxabj\"u\\s" 145 | "cpsjti\"gy" 146 | "aa\"p\\nki\\ajijkqev" 147 | "q\"\"lfdentjgd\\" 148 | "bmokvpoebutfki" 149 | "pielvcbne\xf6efvzxn" 150 | "kx" 151 | "zlgmqagcrbhrwtwtmmg" 152 | "aiyhmntcqjbpv\xb5hhswxbryoedvos" 153 | "tdpaxrb" 154 | "fu\"\x7dttkyvhrlwko" 155 | "oirc\\\"cqlnqffjqt\\k" 156 | "edxlia\\tcyby" 157 | "jpeybgwfayerfrfbvfog\"ol" 158 | "ysr" 159 | "bzwzilgwfugjk" 160 | "tlcc\x75nukvwjgftetjcs\xaecwc" 161 | "dsqssa\"vzrf\"sewbp\\ahhlmhbeihlh" 162 | "qtgmjck\"n\"guki\"gmdivwqxismqj" 163 | "\"f" 164 | "wuorvlovucngbzdszqpikyk" 165 | "dfrdsacoukmgvhbq\"\"iwto" 166 | "\"ey\"ch\\wcgioe\\\"ouvligmsw" 167 | "ciqlszzgs" 168 | "\\tzyrkaoi\"sopjaq" 169 | "lmtnv" 170 | "ar\"fqoroigiertjjlm\"ymgi\\kkjewsxd" 171 | "wehcimlvudpxtamdn\"rwy" 172 | "hr\"zvrwthr\"vruzqfrldn\"b" 173 | "sggekodkiwvym\"mhsco" 174 | "ltlkfbrrdvk\\" 175 | "uut\"sfjnz\"\\ef" 176 | "hxilg\\" 177 | "zsredsiwlzrpedibn" 178 | "vtfi" 179 | "\\h" 180 | "qekfrc\xf6wduodbwrguqcng\\n" 181 | "\"lljlfdrxftwidn\\pkv\xd9ij" 182 | "mrvgqynpehkliuijlpp" 183 | "gikjph" 184 | "yoxcdrdt\"wbaurnyhoyxoihu" 185 | "onmomwuxuammbzxe" 186 | "rnrr\\twviz\x61gqaljr\x0dmtw" 187 | "r\"vupaoi" 188 | "l" 189 | "sei" 190 | "jwxtdtbkd\\kxd" 191 | "\x22v\\" 192 | "ahd" 193 | "j\"bjqxs" 194 | "\\i\x24gglxub\\nzsajokt" 195 | "lviwpu\"uxdlh\\zuy\"xqy\"ytdzlx\"r" 196 | "kptfmys" 197 | "fwxzikfhczkjwyjszqdbkepaeellc" 198 | "nlqpsvbrbd\\ns" 199 | "qryuwkjiodw\"\"vaqyq\"dmyifm" 200 | "tw\x15kdmaudjl\\zorhp\"alwh" 201 | "aatrvczesykekkjfyb\"kb" 202 | "usqcutbqbxxhucwxo\xc1ltb\"j\"bghjcvws" 203 | "ilhsrnzxkz" 204 | "bianqfdfdhvw" 205 | "hqibqs\x7ax\"qoxqoaqtcsz" 206 | "htxtoojbbauztwxuiq\\ngyfy\\obzc" 207 | "rxn\\moxlj" 208 | "mtus\x84erh\"dbe" 209 | "asx\x50huvsitcxadt" 210 | "\"bugggtnrc\"\"kl\"hmpu\x83hqrvhpo" 211 | "ewisbp\"\"vuzf\\w\x5fvalszdhl" 212 | "scusplpwxfnxu\x57\"zynpn\x99xerc\\ri" 213 | "m\\kinmkke\x0cl" 214 | "xhuzit\x7fd" 215 | "kfbo\x04\x50ruqirn" 216 | "t\"\"xpbdscmdoug" 217 | "punvpsgnbgyxe\"sptmpz" 218 | "bxukkazijr" 219 | "nxyrcdaoo\"rjkk\"wntehcvcip\"vrd" 220 | "rdpvqskmihqaw" 221 | "p\\gwdhtqnpgthod" 222 | "nwnuf\"\"yebycearom\"nqym\"\xd4sii\xccle" 223 | "alda\"ptspo\"wkkv\"zoi\"hkb\"vnntyd" 224 | "ixpgpfzbqv" 225 | "znui\"\\fzn\x03qozabh\"rva\"pv\x67" 226 | "e\"zswmwuk" 227 | "hcccygwfa" 228 | "ngmace\\rtyllolr\"\x68bw" 229 | "\\c\"jyufbry\"ryo\"xpo\x26ecninfeckh\\s" 230 | "hdnpngtuc\"dzbvvosn\x31fwtpzbrt" 231 | "hesbpd\xd4" 232 | "dsdbstuzrdfmrnyntufs\"dmv" 233 | "d\xeeibcwhcvkt" 234 | "fvzwrsfjdqdmy\"\"v" 235 | "ns\"dqafz\\lkyoflnazv\"mn\x37\"o\"yj\"e" 236 | "dypilgbwzccayxa\"bnmuernx" 237 | "q\xa9ztqrhreb\"\"kxfeyodqb" 238 | "iz\xa5qjxqulaawuwz\"rqmpcj\\yel" 239 | "z\"\\pq\"\"y\x67zpjtn" 240 | "ifxqvivp\"kiiftdoe" 241 | "jxzebj\"\x35\"qr\"ecglcutuoyywqumcs\"kk" 242 | "q" 243 | "yob\x85qmpuwexptczbkrl" 244 | "cjiavv\"uudpozvibyycnmxhxpxmpjoz" 245 | "xro\\uiqyrcid" 246 | "nod\\k" 247 | "d\"neiec" 248 | "tqyrqvwyvmz\\pzgzzcqsqsrgbqbtapoz" 249 | "r\"xvocpeuxfxslgueb\x05kzyyie\"aoec" 250 | "\"du\\uirlhcbgv\\cjqhfreqnvn" 251 | "zp\x04\x15\"pbjwhrjtmiba" 252 | "\\cv\"" 253 | "k\"rwnb\\hiu\"rqd\"rc\\nyakrhly" 254 | "klrmafjzandiddodgz" 255 | "xipzhqzhvlpykzcuppx" 256 | "zdvrvn\xd0mtfvpylbn\\\\sxcznrzugwznl" 257 | "ody\\pvm\"kpjiudzhxazirgxzvumeat\"o" 258 | "kllvhdp\"prjikzrrc\"adgpegc\\\"gk" 259 | "sqtpug\xbcaauxaamw" 260 | "wegxxrrxdvpivrqievfeokmnojsk" 261 | "\\bo" 262 | "gijhz" 263 | "ylowluvabwrigssdgtxdwsiorxev\xdd" 264 | "\"" 265 | "ghnsrnsqtxpygikahkrl" 266 | "\"rcfqkbjf\"sgxg\"vnd\\rotn" 267 | "ap\"smgsuexjrbuqs\"mpbstogj\"x" 268 | "koaunz\\sgt\"opv" 269 | "yialiuzwix" 270 | "yp\"ndxgwzml\"bt" 271 | "lpcjxmggfsy\\szbxccarjkqzasqkb\xcfd\x0c" 272 | "x" 273 | "mgakc" 274 | "vjieunoh\x73fjwx" 275 | "erbvv\"qulsd" 276 | "mimycrbfhqkarmz" 277 | "tihfbgcszuej\"c\xfbvoqskkhbgpaddioo" 278 | "mziavkwrmekriqghw" 279 | "izk\\tnjd\\ed\\emokvjoc" 280 | "c\"nhbqzndro\\g" 281 | "usfngdo" 282 | "aypljdftvptt" 283 | "ym\"afvq\xbcc" 284 | "zabi\"wjpvugwhl" 285 | "ebvptcjqjhc\"n\"p\"dxrphegr\\" 286 | "mzlqqxokhye\xd9\\rffhnzs" 287 | "hnipqknwpsjakanuewe" 288 | "rqgbfcjdrmiz\"h" 289 | "kzzp\\z\\txmkwaouxictybwx" 290 | "yzmspjkqrteiydswlvb" 291 | "gjpxklgpzv\"txri\\hotpuiukzzzd" 292 | "p\"rxergtbsxmjmkeeqwvoagnki\"" 293 | "santipvuiq" 294 | "\"ihjqlhtwbuy\"hdkiv\"mtiqacnf\\" 295 | "oliaggtqyyx" 296 | "fwwnpmbb" 297 | "yrtdrieazfxyyneo" 298 | "nywbv\\" 299 | "twc\\ehfqxhgomgrgwpxyzmnkioj" 300 | "qludrkkvljljd\\xvdeum\x4e" 301 | -------------------------------------------------------------------------------- /8/problem-a.md: -------------------------------------------------------------------------------- 1 | Space on the sleigh is limited this year, and so Santa will 2 | be bringing his list as a digital copy. He needs to know how 3 | much space it will take up when stored. 4 | 5 | It is common in many programming languages to provide a way 6 | to escape special characters in strings. For example, C, 7 | JavaScript, Perl, Python, and even PHP handle special 8 | characters in very similar ways. 9 | 10 | However, it is important to realize the difference between 11 | the number of characters in the code representation of the 12 | string literal and the number of characters in the in-memory 13 | string itself. 14 | 15 | For example: 16 | 17 | * `""` is 2 characters of code (the two double quotes), but 18 | the string contains zero characters. 19 | * `"abc"` is 5 characters of code, but 3 characters in the 20 | string data. 21 | * `"aaa\"aaa"` is 10 characters of code, but the string itself 22 | contains six "a" characters and a single, escaped quote 23 | character, for a total of 7 characters in the string data. 24 | * `"\x27"` is 6 characters of code, but the string itself 25 | contains just one - an apostrophe ('), escaped using 26 | hexadecimal notation. 27 | 28 | Santa's list is a file that contains many double-quoted 29 | string literals, one on each line. The only escape sequences 30 | used are \\\\ (which represents a single backslash), \" 31 | (which represents a lone double-quote character), and \x 32 | plus two hexadecimal characters (which represents a single 33 | character with that ASCII code). 34 | 35 | Disregarding the whitespace in the file, what is the number 36 | of characters of code for string literals minus the number 37 | of characters in memory for the values of the strings in 38 | total for the entire file? 39 | 40 | For example, given the four strings above, the total number 41 | of characters of string code (2 + 5 + 10 + 6 = 23) minus the 42 | total number of characters in memory for string values (0 + 43 | 3 + 7 + 1 = 11) is 23 - 11 = 12. 44 | -------------------------------------------------------------------------------- /8/problem-b.md: -------------------------------------------------------------------------------- 1 | Now, let's go the other way. In addition to finding the 2 | number of characters of code, you should now encode each 3 | code representation as a new string and find the number of 4 | characters of the new encoded representation, including the 5 | surrounding double quotes. 6 | 7 | For example: 8 | 9 | * `""` encodes to `"\"\""`, an increase from 2 characters to 6. 10 | * `"abc"` encodes to `"\"abc\""`, an increase from 5 11 | characters to 9. 12 | * `"aaa\"aaa"` encodes to `"\"aaa\\\\\\"aaa\""`, an increase 13 | from 10 characters to 16. 14 | * `"\x27"` encodes to `"\"\\\\x27\""`, an increase from 6 15 | characters to 11. 16 | 17 | Your task is to find the total number of characters to 18 | represent the newly encoded strings minus the number of 19 | characters of code in each original string literal. For 20 | example, for the strings above, the total encoded length 21 | (6 + 9 + 16 + 11 = 42) minus the characters in the original 22 | code representation (23, just like in the first part of this 23 | puzzle) is 42 - 23 = 19. 24 | -------------------------------------------------------------------------------- /8/soln.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | 7 | import Soln 8 | 9 | -- | Count the number of characters in a string 10 | -- after escape processing, without actually unescaping the 11 | -- string. 12 | countEscaped :: String -> Int 13 | countEscaped s = 14 | envelop countNext 0 s 15 | where 16 | countNext n "" = (n, "") 17 | countNext n ('\\' : '\\' : xs) = (n + 1, xs) 18 | countNext n ('\\' : '"' : xs) = (n + 1, xs) 19 | countNext n ('\\' : 'x' : d1 : d2 : xs) 20 | | isHexDigit d1 && isHexDigit d2 = (n + 1, xs) 21 | | otherwise = error "bad hex escape" 22 | countNext _ ('\\' : _) = error "bad backslash escape" 23 | countNext n (_ : xs) = (n + 1, xs) 24 | 25 | -- | Adjust for the enclosing quotes in the source string. 26 | countStringEscaped :: String -> Int 27 | countStringEscaped s 28 | | head s == '"' && last s == '"' = 29 | countEscaped s - 2 30 | | otherwise = error "bad string" 31 | 32 | -- | Strategy: Brute force. 33 | solna :: String -> IO () 34 | solna stuff = do 35 | print $ sum $ map diff $ lines stuff 36 | where 37 | diff s = length s - countStringEscaped s 38 | 39 | -- | Count the number of characters needed to 40 | -- successfully escape a string, without actually 41 | -- doing the escaping. 42 | countEscapesString :: String -> Int 43 | countEscapesString s = 44 | envelop countEscapes 2 s 45 | where 46 | countEscapes n "" = (n, "") 47 | countEscapes n ('"' : xs) = (n + 2, xs) 48 | countEscapes n ('\\' : xs) = (n + 2, xs) 49 | countEscapes n (_ : xs) = (n + 1, xs) 50 | 51 | -- | Strategy: Brute force. 52 | solnb :: String -> IO () 53 | solnb stuff = do 54 | print $ sum $ map diff $ lines stuff 55 | where 56 | diff s = countEscapesString s - length s 57 | 58 | main :: IO () 59 | main = makeMain solna solnb 60 | -------------------------------------------------------------------------------- /9/Makefile: -------------------------------------------------------------------------------- 1 | ../Soln/Makefile.template -------------------------------------------------------------------------------- /9/README.md: -------------------------------------------------------------------------------- 1 | This is a good example of a problem for which way more 2 | efficient solutions (by large constant factors) than brute 3 | force are available, but for which brute force is good 4 | enough. 5 | 6 | The Haskell solution makes use of `tile` from `Soln` to 7 | compute distances of paths. An alternative would be to 8 | `foldr` over a tuple containing the current location and the 9 | accumulated distance. 10 | -------------------------------------------------------------------------------- /9/input.txt: -------------------------------------------------------------------------------- 1 | AlphaCentauri to Snowdin = 66 2 | AlphaCentauri to Tambi = 28 3 | AlphaCentauri to Faerun = 60 4 | AlphaCentauri to Norrath = 34 5 | AlphaCentauri to Straylight = 34 6 | AlphaCentauri to Tristram = 3 7 | AlphaCentauri to Arbre = 108 8 | Snowdin to Tambi = 22 9 | Snowdin to Faerun = 12 10 | Snowdin to Norrath = 91 11 | Snowdin to Straylight = 121 12 | Snowdin to Tristram = 111 13 | Snowdin to Arbre = 71 14 | Tambi to Faerun = 39 15 | Tambi to Norrath = 113 16 | Tambi to Straylight = 130 17 | Tambi to Tristram = 35 18 | Tambi to Arbre = 40 19 | Faerun to Norrath = 63 20 | Faerun to Straylight = 21 21 | Faerun to Tristram = 57 22 | Faerun to Arbre = 83 23 | Norrath to Straylight = 9 24 | Norrath to Tristram = 50 25 | Norrath to Arbre = 60 26 | Straylight to Tristram = 27 27 | Straylight to Arbre = 81 28 | Tristram to Arbre = 90 29 | -------------------------------------------------------------------------------- /9/problem-a.md: -------------------------------------------------------------------------------- 1 | Every year, Santa manages to deliver all of his presents in 2 | a single night. 3 | 4 | This year, however, he has some new locations to visit; his 5 | elves have provided him the distances between every pair of 6 | locations. He can start and end at any two (different) 7 | locations he wants, but he must visit each location exactly 8 | once. What is the shortest distance he can travel to achieve 9 | this? 10 | 11 | For example, given the following distances: 12 | 13 | London to Dublin = 464 14 | London to Belfast = 518 15 | Dublin to Belfast = 141 16 | 17 | The possible routes are therefore: 18 | 19 | Dublin -> London -> Belfast = 982 20 | London -> Dublin -> Belfast = 605 21 | London -> Belfast -> Dublin = 659 22 | Dublin -> Belfast -> London = 659 23 | Belfast -> Dublin -> London = 605 24 | Belfast -> London -> Dublin = 982 25 | 26 | The shortest of these is 27 | 28 | London -> Dublin -> Belfast = 605 29 | 30 | and so the answer is 605 in this example. 31 | 32 | What is the distance of the shortest route? 33 | -------------------------------------------------------------------------------- /9/problem-b.md: -------------------------------------------------------------------------------- 1 | The next year, just to show off, Santa decides to take the 2 | route with the longest distance instead. 3 | 4 | He can still start and end at any two (different) locations 5 | he wants, and he still must visit each location exactly 6 | once. 7 | 8 | For example, given the distances above, the longest route 9 | would be 982 via (for example) `Dublin -> London -> 10 | Belfast`. 11 | 12 | What is the distance of the longest route? 13 | -------------------------------------------------------------------------------- /9/soln.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | 7 | import qualified Data.Map.Strict as M 8 | import qualified Data.Set as S 9 | 10 | import Soln 11 | 12 | -- | Map pairs of cities to their distances. 13 | type DistMap = M.Map (String, String) Int 14 | 15 | -- | Parse a route. This pattern is common in the 16 | -- remaining problems. 17 | parseRoute :: [String] -> (String, String, Int) 18 | parseRoute [c1, "to", c2, "=", ds] = (c1, c2, read ds) 19 | parseRoute _ = error "bad route" 20 | 21 | -- | Parse the input and construct a distance map. 22 | parseMap :: String -> ([String], DistMap) 23 | parseMap stuff = 24 | let triples = map parseRoute $ map words $ lines stuff in 25 | let cities = S.toList $ foldl' findCities S.empty triples in 26 | let distMap = foldl' insertRoute M.empty triples in 27 | (cities, distMap) 28 | where 29 | insertRoute distMap (c1, c2, d) = 30 | M.insert (c1, c2) d $ M.insert (c2, c1) d distMap 31 | findCities citySet (c1, c2, _) = 32 | S.insert c1 $ S.insert c2 citySet 33 | 34 | -- | Compute the total distance of a route. 35 | dist :: DistMap -> [String] -> Int 36 | dist distMap route = 37 | sum $ map legDist $ tiles 2 1 route 38 | where 39 | legDist [c1, c2] = 40 | case M.lookup (c1, c2) distMap of 41 | Just d -> d 42 | Nothing -> error "bad leg" 43 | legDist _ = error "internal error: leg length" 44 | 45 | -- | Type for function that computes the best 46 | -- value in a list given an ordering function. 47 | type BestBy a = (a -> a -> Ordering) -> [a] -> a 48 | 49 | -- | Strategy: Compute all possible paths 50 | -- covering all the cities, then find the 51 | -- best one. Scales geometrically in the 52 | -- number of cities. 53 | soln :: BestBy [String] -> String -> IO () 54 | soln bestBy stuff = do 55 | let (cities, distMap) = parseMap stuff 56 | let optRoute = bestBy (comparing (dist distMap)) $ permutations cities 57 | print $ dist distMap optRoute 58 | 59 | solna :: String -> IO () 60 | solna stuff = soln minimumBy stuff 61 | 62 | solnb :: String -> IO () 63 | solnb stuff = soln maximumBy stuff 64 | 65 | main :: IO () 66 | main = makeMain solna solnb 67 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Copyright © 2015 Bart Massey 2 | 3 | [This program is licensed under the "MIT License"] 4 | 5 | Permission is hereby granted, free of charge, to any person 6 | obtaining a copy of this software and associated 7 | documentation files (the "Software"), to deal in the 8 | Software without restriction, including without limitation 9 | the rights to use, copy, modify, merge, publish, distribute, 10 | sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, 12 | subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall 15 | be included in all copies or substantial portions of the 16 | Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 19 | KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 20 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 21 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 22 | OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 23 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 24 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 25 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright © 2015 Bart Massey 2 | # [This program is licensed under the "MIT License"] 3 | # Please see the file COPYING in the source 4 | # distribution of this software for license terms. 5 | 6 | all: 7 | for d in `seq 1 25`; do ( cd $$d && make ) ; done 8 | 9 | clean: 10 | for d in `seq 1 25`; do ( cd $$d && make clean ) ; done 11 | cd Soln && make clean 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Advent Of Code 2015: Tutorial Solutions in Haskell 2 | Copyright (c) 2015 Bart Massey 3 | 4 | Herein lie solutions to all of the problems of the 2015 5 | [Advent of Code](http://adventofcode.com). Advent of Code 6 | was a fantastic exercise, and I thank the author and others 7 | involved profusely for their excellent work. Thanks also to 8 | `relsqui` for pointing me at this on about Day 9. I had some 9 | catching up to do. 10 | 11 | For each solution, I have included commented and cleaned-up 12 | Haskell code. The solution file will always be `soln.hs`, 13 | and the `Makefile` will compile it to `soln` using 14 | [GHC](http://www.haskell.org/ghc/). There is a `README.md` 15 | in every problem directory containing descriptions and 16 | comments. I have also included the problem descriptions 17 | (`problem-a.md` and `problem-b.md`) and my specific 18 | `input.txt` for posterity. 19 | 20 | I assume you have GHC running on a fast-ish UNIX box with a 21 | bunch of memory (although most everything should also work 22 | on other operating systems). For a few problems you will 23 | also need to install extra packages from 24 | [Hackage](http://hackage.haskell.org). The easiest way to do 25 | that is with 26 | [`cabal`](https://wiki.haskell.org/Cabal-Install) AKA 27 | `cabal-install`, so you will want to learn how to operate 28 | that. 29 | 30 | The goals of these solutions are to: 31 | 32 | * Provide canonical correct solutions with reasonable 33 | runtimes. 34 | 35 | * Illustrate reasonable solution strategies. 36 | 37 | * Illustrate the use of Haskell in problem-solving, 38 | including some "advanced" techniques that aren't really 39 | advanced and should be part of every Haskell programmer's 40 | repertoire. 41 | 42 | I learned a ton of Haskell and a little bit of software 43 | engineering I should already have known writing these. 44 | 45 | These solutions deserve a much more thorough top-level 46 | description than I have the energy to write at this point. 47 | I will revise this file in the indefinite future. 48 | 49 | I am under no illusions that I am a superior Haskell 50 | programmer. Indeed, I suspect certain members of the 51 | Haskell community will be more amused than impressed by my 52 | fairly straightforward solutions. Feedback and pull requests 53 | are extremely welcome! Let me know what I should have done, 54 | and I'll try to make it right. 55 | 56 | This work is licensed under the "MIT License". Please see 57 | the file `COPYING` in the source distribution of this software 58 | for license terms. 59 | 60 | -------------------------------------------------------------------------------- /Soln/Makefile: -------------------------------------------------------------------------------- 1 | # Copyright © 2015 Bart Massey 2 | # [This program is licensed under the "MIT License"] 3 | # Please see the file COPYING in the source 4 | # distribution of this software for license terms. 5 | 6 | HC = ghc 7 | HCFLAGS = -Wall -O2 8 | 9 | Soln.hi Soln.o: Soln.hs 10 | $(HC) $(HCFLAGS) --make Soln.hs 11 | 12 | clean: 13 | -rm -f Soln.hi Soln.o 14 | -------------------------------------------------------------------------------- /Soln/Makefile.template: -------------------------------------------------------------------------------- 1 | # Copyright © 2015 Bart Massey 2 | # [This program is licensed under the "MIT License"] 3 | # Please see the file COPYING in the source 4 | # distribution of this software for license terms. 5 | 6 | HC = ghc 7 | HCFLAGS = -i../Soln -Wall -O2 8 | 9 | soln: soln.hs 10 | $(HC) $(HCFLAGS) --make soln.hs 11 | 12 | clean: 13 | -rm -f soln.hi soln.o soln 14 | ( cd ../Soln && make clean ) 15 | -------------------------------------------------------------------------------- /Soln/Soln.hs: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | -- [This program is licensed under the "MIT License"] 3 | -- Please see the file COPYING in the source 4 | -- distribution of this software for license terms. 5 | 6 | 7 | module Soln ( 8 | tiles, envelop, splits, sspan, loop, 9 | makeMain, 10 | module Control.Monad, 11 | module Control.Applicative, 12 | module Data.Bits, 13 | module Data.Char, 14 | module Data.Either, 15 | module Data.Function, 16 | module Data.List, 17 | module Data.Maybe, 18 | module Data.Ord, 19 | module Data.Word ) 20 | where 21 | 22 | import Control.Monad 23 | import Control.Applicative 24 | import Data.Bits 25 | import Data.Char 26 | import Data.Either 27 | import Data.Function 28 | import Data.List 29 | import Data.Maybe 30 | import Data.Ord 31 | import Data.Word 32 | 33 | import System.Environment (getArgs) 34 | 35 | -- | Left fold of accumulating function onto a list. 36 | -- Runs until the accumulating function has consumed 37 | -- the list, then returns the accumulator. Generalizes 38 | -- 'Data.List.mapAccumL'. 39 | envelop :: (b -> [a] -> (b, [a])) -> b -> [a] -> b 40 | envelop _ a [] = a 41 | envelop f a xs = uncurry (envelop f) (f a xs) 42 | 43 | -- | Given a tile size and a skip count, break 44 | -- a list into tiles. The tiles may overlap or 45 | -- be disjoint. Partial tiles are discarded. 46 | tiles :: Int -> Int -> [a] -> [[a]] 47 | tiles size skip xs = 48 | envelop tile [] xs 49 | where 50 | tile partial rest 51 | | length rest < size = (partial, []) 52 | | otherwise = (partial ++ [take size rest], drop skip rest) 53 | 54 | -- | Given a list, return the list of all possible 55 | -- ways to split that list into two parts. 56 | splits :: [a] -> [([a], [a])] 57 | splits xs = zip (inits xs) (tails xs) 58 | 59 | 60 | -- | Like 'Data.List.span', but includes the matched element 61 | -- on the left. 62 | sspan :: (a -> Bool) -> [a] -> ([a], [a]) 63 | sspan _ [] = ([], []) 64 | sspan f (x : xs) | f x = ([x], xs) 65 | sspan f (x : xs) = let (l, r) = sspan f xs in (x : l, r) 66 | 67 | -- | Loop on an 'Either' monad until 'Left' is hit. 68 | loop :: (a -> Either b a) -> a -> b 69 | loop a0 x0 = 70 | case go a0 x0 of 71 | Left y -> y 72 | Right _ -> error "early loop termination" 73 | where 74 | go a x = a x >>= go a 75 | 76 | -- | Package up all the input reading and argument parsing 77 | -- into a single clean `main`. 78 | makeMain :: (String -> IO ()) -> (String -> IO ()) -> IO () 79 | makeMain solna solnb = do 80 | args <- getArgs 81 | stuff <- readFile "input.txt" 82 | case length args of 83 | 0 -> solna stuff 84 | 1 -> solnb stuff 85 | _ -> error "too many args" 86 | -------------------------------------------------------------------------------- /Soln/soln.hs.template: -------------------------------------------------------------------------------- 1 | -- Copyright © 2015 Bart Massey 2 | 3 | import Soln 4 | 5 | solna :: String -> IO () 6 | solna stuff = do 7 | putStrLn stuff 8 | 9 | solnb :: String -> IO () 10 | solnb stuff = do 11 | putStrLn stuff 12 | 13 | main :: IO () 14 | main = makeMain solna solnb 15 | --------------------------------------------------------------------------------