├── day-10.spreadsheet.md ├── day-5.perl ├── day-18.dyalog ├── day-14.py ├── day-3.ijs ├── day-12.befunge ├── day-8.hs ├── day-9.kt ├── day-13.rb ├── day-11.em ├── day-4.fs ├── day-6.c ├── day-1.go ├── day-7.cob ├── day-2.pl ├── day-16.idr └── day-15.rs /day-10.spreadsheet.md: -------------------------------------------------------------------------------- 1 | (this one lives on [Google Sheets](https://docs.google.com/spreadsheets/d/17zqtwu3Vq8VDEznEe3IT97C8nHcBvNRfcATrWvLwQYs/edit?usp=sharing)) 2 | -------------------------------------------------------------------------------- /day-5.perl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | # https://adventofcode.com/2018/day/5 in Perl 5 3 | # Try it on https://tio.run/#perl (put input in the Input field) 4 | 5 | use List::Util qw(min); 6 | my $re = join '|', map {$_.uc, uc.$_} a..z; 7 | my $in = $_ = <>; 8 | 1 while s/$re//g; 9 | print length, "\n"; 10 | print min map {my $v = $in; 1 while $v =~ s/$re|$_|\u$_//g; length $v} a..z; 11 | -------------------------------------------------------------------------------- /day-18.dyalog: -------------------------------------------------------------------------------- 1 | ⍝ https://adventofcode.com/2018/day/18 in Dyalog APL. 2 | ⎕IO ← 0 3 | area ← 50 50 ↑ 50 51 ⍴ '...input goes here...' 4 | tl ← {+⌿(,⍵)∘.='|#'} 5 | rule ← {X←⍵[⊂1 1] ⋄ T L←(tl ⍵)-(tl X) ⋄ X='.':'.|'[T≥3] ⋄ X='|':'|#'[L≥3] ⋄ '.#'[0≠T⌊L]} 6 | ×/ tl (rule⌺3 3⍣10) area 7 | 8 | ⍝ Part 2: 9 | tls ← {⍺=0:⍬⋄ (⊂tl ⍵),(⍺-1)∇step ⍵} 10 | history ← 1000 tls area 11 | ⎕ ← history ⍝ I eyeballed the eventual period-28 oscillation. 12 | 13 | i ← 999+28|(1000000000-999) 14 | ⎕← ×/ tl (step⍣i) area 15 | -------------------------------------------------------------------------------- /day-14.py: -------------------------------------------------------------------------------- 1 | # https://adventofcode.com/2018/day/14 in Python 2 | i, j = 0, 1 3 | a = [3, 7] 4 | k = 640441 5 | 6 | recipes = where = None 7 | while not (recipes and where): 8 | t = a[i] + a[j] 9 | a += [1, t%10] if t > 9 else [t] 10 | i = (i + a[i] + 1) % len(a) 11 | j = (j + a[j] + 1) % len(a) 12 | 13 | if len(a) >= k + 10: 14 | recipes = a[k:k+10] 15 | 16 | last = ''.join(map(str, a[-7:])) 17 | if str(k) in last: 18 | where = last.index(str(k)) + len(a) - 7 19 | 20 | print(recipes, where) 21 | -------------------------------------------------------------------------------- /day-3.ijs: -------------------------------------------------------------------------------- 1 | NB. https://adventofcode.com/2018/day/3 in J 2 | NB. Try it here: https://tio.run/#j (paste the problem input in the "Input" field) 3 | 4 | NB. Parse the input: 5 | input =. stdin '' 6 | junk =. I. input e. '#@,:x' 7 | data =. , ".each cutopen ' ' junk} input 8 | 9 | NB. Now data is a boxed list, and each entry is a 5-vector of numbers. 10 | NB. Let's turn one of those into a 1000x1000 boolean matrix: 11 | rect =. monad : 0 12 | 'I X Y W H' =. y 13 | (-Y,X) |. 1000 1000 {. (H,W) $ 1 14 | ) 15 | 16 | NB. Part A: How many squares covered by overlap? 17 | NB. (Sum all the rects and count >1s.) 18 | total =. +/ > rect each data 19 | echo +/, total>1 20 | 21 | NB. Part B: Which ID doesn't overlap anything? 22 | NB. (Retrieve all the rects back from the total and check which is all 1s.) 23 | check =. dyad : 0 24 | 'I X Y W H' =. y 25 | I* */,1= (H,W) {. (Y,X) |. x 26 | ) 27 | echo >./ >(total&check) each data 28 | -------------------------------------------------------------------------------- /day-12.befunge: -------------------------------------------------------------------------------- 1 | ^----------adventofcode.com/2018/day/12 part 1 in Befunge-93-------------------- 2 | ................................. ................................. 3 | ................................. ................................. 4 | ................................. ................................. 5 | ................................. ................................. 6 | ................................. ................................. 7 | **Lookup table data goes here!** Count pots and print answer. 8 | ( <- iter count v+1p+2/"!"\%"!":\< >00g.@ 9 | >~~~~~~~~~~~~~~~>0>:~:" "` | | !+*45:-1p00+g00< 10 | ^skip input header v >:::"!"%\"!"/1+g"#"-!\"!"-*^ 11 | #p6/2\~$$$~~~$_v#!`" ":~*2<0_v#+1:~<<< 12 | v ^ v ^**854p000< 13 | >"#"`+ ^ v v < 14 | >_p>" "1->2*48*%00g:"!"%\"!"/1 +g"#"`+:6gv 28850s 15 | >:::"!"%49*+\"!"/1+gv >17g:2-17p!#^_ # 854** 16 | |:-1 p+1/"!"\%"!":\ < |! -**854p00:+1:g00 < 17 | > 000p^ >2-:"!"%49*+\"!"/1+p ^ 18 | Copy back buffer Compute generation of automaton 19 | -------------------------------------------------------------------------------- /day-8.hs: -------------------------------------------------------------------------------- 1 | -- https://adventofcode.com/2018/day/8 in Haskell 2 | -- (rolling our own parser combinators, à la http://dev.stephendiehl.com/fun/002_parsers.html) 3 | -- Try it here: https://tio.run/#haskell 4 | 5 | {-# LANGUAGE DeriveFunctor #-} 6 | {-# LANGUAGE LambdaCase #-} 7 | 8 | import Control.Monad 9 | 10 | newtype Parser t a = Parser { parse :: [t] -> [(a, [t])] } deriving (Functor) 11 | 12 | instance Applicative (Parser t) where 13 | pure a = Parser (\s -> [(a, s)]) 14 | (<*>) = ap 15 | 16 | instance Monad (Parser t) where 17 | p >>= f = Parser (\s -> do (a, s') <- parse p s 18 | parse (f a) s') 19 | item :: Parser t t 20 | item = Parser $ \case [] -> [] 21 | (t:ts) -> [(t,ts)] 22 | 23 | runParser :: Parser t a -> [t] -> a 24 | runParser m s = 25 | case parse m s of 26 | [(a, [])] -> a 27 | [(_, rest)] -> error "Parser did not consume entire stream." 28 | _ -> error "Couldn't parse." 29 | 30 | ----------------------------------------------------------------------- 31 | 32 | data Node = 33 | Node { children :: [Node] 34 | , metadata :: [Int] } 35 | 36 | parseNode :: Parser Int Node 37 | parseNode = do 38 | c <- item 39 | m <- item 40 | children <- replicateM c parseNode 41 | metadata <- replicateM m item 42 | pure $ Node children metadata 43 | 44 | sumMetadata :: Node -> Int 45 | sumMetadata (Node c m) = sum m + sum (map sumMetadata c) 46 | 47 | value :: Node -> Int 48 | value (Node [] m) = sum m 49 | value (Node cs m) = sum [value $ cs!!(i-1) | i <- m, i >= 1, i <= length cs] 50 | 51 | main = do 52 | stream <- map read . words <$> getLine 53 | let node = runParser parseNode stream 54 | print (sumMetadata node) 55 | print (value node) 56 | -------------------------------------------------------------------------------- /day-9.kt: -------------------------------------------------------------------------------- 1 | // https://adventofcode.com/2018/day/9 in Kotlin 2 | // Try it here: https://try.kotlinlang.org 3 | // (Set execution mode to "JavaScript" — it runs out of memory on "JVM".) 4 | 5 | class Marble(val points: Int) { 6 | var left: Marble = this 7 | var right: Marble = this 8 | 9 | // Insert a marble to the right of this one. 10 | fun insert_right(points: Int) { 11 | val old: Marble = right // ··· ⇆ this ⇆ old ⇆ ··· 12 | right = Marble(points) // ··· ⇆ this → right old ⇆ ··· 13 | right.left = this // ··· ⇆ this ⇆ right old ⇆ ··· 14 | right.right = old // ··· ⇆ this ⇆ right → old ⇆ ··· 15 | old.left = right // ··· ⇆ this ⇆ right ⇆ old ⇆ ··· 16 | } 17 | 18 | // Remove this marble from the cycle and return its point value. 19 | fun pop(): Int { 20 | left.right = right 21 | right.left = left 22 | return points 23 | } 24 | } 25 | 26 | class Game(val players: Int, val last_marble: Int) { 27 | var score: Array = Array(players) { 0L } 28 | var current: Marble = Marble(0) 29 | var whose_turn: Int = 0 30 | var marble_value: Int = 1 31 | 32 | // Get the next available marble's value. 33 | fun next_marble_value() = marble_value++ 34 | 35 | // Check if the game has ended. 36 | fun game_over() = marble_value == last_marble 37 | 38 | // Award points to the current player. 39 | fun award_points(points: Int) { 40 | score[whose_turn] += points.toLong() 41 | } 42 | 43 | // Advance the turn to the next player. 44 | fun advance_turn() { 45 | whose_turn = (whose_turn + 1) % players 46 | } 47 | 48 | // Simulate a turn of the game. 49 | fun turn() { 50 | val m = next_marble_value() 51 | if (m % 23 == 0) { 52 | current = current.left.left.left.left.left.left.left 53 | val taken = current.pop() 54 | current = current.right 55 | award_points(taken + m) 56 | } else { 57 | current = current.right 58 | current.insert_right(m) 59 | current = current.right 60 | } 61 | advance_turn() 62 | } 63 | 64 | // Simulate the whole game and return the final scores. 65 | fun simulate(): Array { 66 | while (!game_over()) turn() 67 | return score 68 | } 69 | } 70 | 71 | fun main() { 72 | println(Game(459, 71320).simulate().max()) 73 | println(Game(459, 7132000).simulate().max()) 74 | } 75 | -------------------------------------------------------------------------------- /day-13.rb: -------------------------------------------------------------------------------- 1 | # https://adventofcode.com/2018/day/13 in Ruby. 2 | 3 | class TurnPlan; end 4 | class Left < TurnPlan; def next; Straight.new; end; end 5 | class Straight < TurnPlan; def next; Right.new; end; end 6 | class Right < TurnPlan; def next; Left.new; end; end 7 | 8 | class Direction 9 | def turn(signal) 10 | case signal 11 | when '/' then slash 12 | when '\\' then backslash 13 | when Straight then self 14 | when Left then left 15 | when Right then right 16 | end 17 | end 18 | 19 | def self.parse(character) 20 | case character 21 | when '^' then North.new 22 | when '>' then East.new 23 | when 'v' then South.new 24 | when '<' then West.new 25 | end 26 | end 27 | end 28 | 29 | class North < Direction 30 | def x; 0; end 31 | def y; -1; end 32 | def left; West.new; end # ⮢ 33 | def right; East.new; end # ⮣ 34 | def slash; East.new; end # ⮣ 35 | def backslash; West.new; end # ⮢ 36 | end 37 | 38 | class East < Direction 39 | def x; 1; end 40 | def y; 0; end 41 | def left; North.new; end # ⮥ 42 | def right; South.new; end # ⮧ 43 | def slash; North.new; end # ⮥ 44 | def backslash; South.new; end # ⮧ 45 | end 46 | 47 | class South < Direction 48 | def x; 0; end 49 | def y; 1; end 50 | def left; East.new; end # ⮡ 51 | def right; West.new; end # ⮠ 52 | def slash; West.new; end # ⮠ 53 | def backslash; East.new; end # ⮡ 54 | end 55 | 56 | class West < Direction 57 | def x; -1; end 58 | def y; 0; end 59 | def left; South.new; end # ⮦ 60 | def right; North.new; end # ⮤ 61 | def slash; South.new; end # ⮦ 62 | def backslash; North.new; end # ⮤ 63 | end 64 | 65 | class Cart 66 | attr_reader :x, :y 67 | 68 | def initialize(x, y, dir) 69 | @x, @y, @dir, @plan = x, y, dir, Left.new 70 | end 71 | 72 | def ahead 73 | @x += @dir.x 74 | @y += @dir.y 75 | end 76 | 77 | def turn(signal) 78 | case signal 79 | when '+' then turn(@plan); @plan = @plan.next 80 | else @dir = @dir.turn(signal) 81 | end 82 | end 83 | end 84 | 85 | carts = [] 86 | world = Hash.new(nil) 87 | 88 | ARGF.each_with_index do |line, y| 89 | line.chars.each_with_index do |ch, x| 90 | world[[x, y]] = ch if '+\/'.include?(ch) 91 | (dir = Direction.parse(ch)) and carts.push(Cart.new(x, y, dir)) 92 | end 93 | end 94 | 95 | collisions = [] 96 | 97 | until carts.size <= 1 98 | # Simulate a tick. 99 | carts.sort_by { |c| [c.y, c.x] }.each do |c| 100 | c.ahead 101 | (signal = world[[c.x, c.y]]) and c.turn(signal) 102 | collide = ->(o) { [o.x, o.y] == [c.x, c.y] } 103 | if carts.count(&collide) > 1 104 | collisions.push [c.x, c.y] 105 | carts.delete_if(&collide) 106 | end 107 | end 108 | end 109 | 110 | p collisions[0] 111 | p carts 112 | -------------------------------------------------------------------------------- /day-11.em: -------------------------------------------------------------------------------- 1 | # https://adventofcode.com/2018/day/11 in Emily (http://emilylang.org/). 2 | \version 0.2 3 | 4 | # Some preliminary definitions: 5 | inf = 1/0 6 | abs ^n = n < 0 ? ~n : n 7 | fixup ^n = abs n < 0.00000000001 ? 0 : n 8 | floor ^n = fixup (n - n % 1) # floor 3.14 = 3 9 | hundreds ^n = floor (n % 1000 / 100) # hundreds 98765.432 = 7 10 | 11 | for ^lo ^hi ^f = { i = lo; while ^(i <= hi) ^(f i; i = i + 1) } 12 | 13 | # Input: grid serial number. 14 | gsn = 1309 15 | 16 | # Power of a fuel cell. 17 | power ^x ^y = { 18 | rackID = x + 10 19 | hundreds ((rackID * y + gsn) * rackID) - 5 20 | } 21 | 22 | # Prototype for a memoized 2D grid of numbers. 23 | # (Emily doesn't have tuples, so we store (x:3, y:4) as key 3004.) 24 | grid = [ 25 | store ^x ^y ^v = { this.let (x*1000+y) v; v } 26 | fetch ^x ^y = this.has (x*1000+y) ? this (x*1000+y) : this.store x y (this.compute x y) 27 | ] 28 | 29 | # A grid of fuel cell power values. 30 | powerGrid = [ 31 | parent = grid 32 | compute ^x ^y = power x y 33 | ] 34 | 35 | # rectGrid(x)(y) equals Σi=1..x Σj=1..y powerGrid(i)(j). 36 | # 37 | # This helps us sum rectangles on the grid efficiently. Suppose 38 | # 39 | # i j 40 | # | | 41 | # ⎡ A A A A B B ··· 42 | # ⎢ A A A A B B 43 | # powerGrid = k ——⎢ A A A A B B 44 | # ⎢ C C C C D D 45 | # l ——⎢ C C C C D D 46 | # ⎢ . . 47 | # ⎢ : · 48 | # 49 | # And we wish to sum the "D" values. Then 50 | # 51 | # rectGrid(i,k) = A 52 | # rectGrid(j,k) = A+B 53 | # rectGrid(i,l) = A+C 54 | # rectGrid(j,l) = A+B+C+D 55 | # 56 | # So that rectGrid(j,l) − rectGrid(i,l) − rectGrid(j,k) + rectGrid(i,k) = D. 57 | # This is the formula used by `square s x y` to sum a square of fuel powers. 58 | # 59 | # We use the same principle (with D 1×1) to compute rectGrid's values: 60 | # 61 | rectGrid = [ 62 | parent = grid 63 | compute ^x ^y = { 64 | (x == 0 || y == 0) ? 0 : { 65 | a = this.fetch 66 | a(x)(y-1) + a(x-1)(y) - a(x-1)(y-1) + powerGrid.fetch(x)(y) 67 | } 68 | } 69 | ] 70 | 71 | # Sum an s×s square on the grid, with top-left corner at (x,y). 72 | square ^s ^x ^y = { 73 | a = rectGrid.fetch 74 | a(x+s-1)(y+s-1) - a(x-1)(y+s-1) - a(x+s-1)(y-1) + a(x-1)(y-1) 75 | } 76 | 77 | # Find the maximum square in a grid, subject to constraints on (size, x, y). 78 | maximize ^slo ^shi ^xlo ^xhi ^ylo ^yhi = { 79 | best = [ value = ~inf ] 80 | for slo shi ^s ( 81 | for xlo (xhi-s+1) ^x ( 82 | for ylo (yhi-s+1) ^y ( 83 | value = square s x y 84 | if (value > best.value) ^( 85 | best = [ x = x; y = y; size = s; value = value ] 86 | ) 87 | ) 88 | ) 89 | ) 90 | best 91 | } 92 | 93 | answer = (maximize 3 3 1 300 1 300) 94 | print (answer.x) "," (answer.y) ln 95 | 96 | answer = (maximize 1 300 1 300 1 300) 97 | print (answer.x) "," (answer.y) "," (answer.size) ln 98 | -------------------------------------------------------------------------------- /day-4.fs: -------------------------------------------------------------------------------- 1 | // https://adventofcode.com/2018/day/4 in F#. 2 | // Try it here: https://tio.run/#fs-core 3 | 4 | open System 5 | open System.Text.RegularExpressions 6 | open System.Collections.Generic 7 | 8 | // A helper to update a Dictionary with a function and some default value. 9 | let update (dict: Dictionary<'K, 'V>) (key: 'K) (updater: 'V -> 'V) (defaultValue: 'V): unit = 10 | dict.[key] <- updater (if dict.ContainsKey key then dict.[key] else defaultValue) 11 | 12 | type GuardID = int 13 | type Event = 14 | | BeginShift of GuardID * DateTime 15 | | Sleep of DateTime 16 | | Wake of DateTime 17 | 18 | let timeOf: Event -> DateTime = function 19 | | BeginShift (_, t) -> t 20 | | Wake t -> t 21 | | Sleep t -> t 22 | 23 | // Parse a single line of the input. 24 | let parseEvent (line: string): Event = 25 | let m = Regex.Match(line, @"\[(....-..-.. ..:..)\] (.*)") 26 | let time = DateTime.Parse m.Groups.[1].Value 27 | match m.Groups.[2].Value with 28 | | "falls asleep" -> Sleep time 29 | | "wakes up" -> Wake time 30 | | body -> let guardID = int(Regex.Match(body, @"\d+").Value) 31 | BeginShift (guardID, time) 32 | 33 | // Get events and sort them chronologically. 34 | let events = 35 | Seq.initInfinite (fun _ -> System.Console.In.ReadLine()) 36 | |> Seq.takeWhile ((<>) null) 37 | |> Seq.map parseEvent 38 | |> Seq.sortBy timeOf 39 | 40 | // We'll write some stateful code to analyze the input. 41 | type MinuteAsleep = int 42 | let mutable currentGuard : GuardID = -1 43 | let mutable sleepStart : Option = None 44 | let mutable totalSleep = new Dictionary() 45 | let mutable sleepSchedule = new Dictionary>() 46 | 47 | for event in events do 48 | match event with 49 | | BeginShift (g, _) -> 50 | currentGuard <- g 51 | sleepStart <- None // Guard is awake at start of shift 52 | | Sleep t -> 53 | sleepStart <- Some(t) 54 | | Wake tEnd -> 55 | // Guh…? How long was I out for? 56 | let tStart = Option.get sleepStart 57 | sleepStart <- None 58 | let minutes = tEnd.Minute - tStart.Minute 59 | 60 | // Oh no, this nap will count toward my running total… 61 | update totalSleep currentGuard ((+) minutes) 0 62 | 63 | // *And* to the boss's schedule of which minutes I've been dozing off on most… 64 | update sleepSchedule currentGuard id (new Dictionary()) 65 | for m in { tStart.Minute .. tEnd.Minute - 1 } do 66 | update sleepSchedule.[currentGuard] m ((+) 1) 0 67 | 68 | // Strategy 1: get the overall sleepiest guard, and then their sleepiest minute. 69 | let maxByValue d = Seq.maxBy (fun (KeyValue(_, v)) -> v) d 70 | let sleepiestGuard = (maxByValue totalSleep).Key 71 | let sleepiestMinute = (maxByValue sleepSchedule.[sleepiestGuard]).Key 72 | 73 | printfn "%d" (sleepiestGuard * sleepiestMinute) 74 | 75 | // Strategy 2: find the most frequent asleep (guard, minute) pair. 76 | let (reliableGuard, reliableMinute) = 77 | sleepSchedule 78 | |> Seq.map (fun (KeyValue(guard, dict)) -> 79 | // From each guard's schedule, get their sleepiest minute and its frequency. 80 | let (KeyValue (minute, freq)) = maxByValue dict 81 | ((guard, minute), freq) 82 | ) 83 | |> Seq.maxBy (fun (_, freq) -> freq) |> fst 84 | 85 | printfn "%d" (reliableGuard * reliableMinute) 86 | -------------------------------------------------------------------------------- /day-6.c: -------------------------------------------------------------------------------- 1 | /* https://adventofcode.com/2018/day/6 in rustic, homespun C89. */ 2 | #include 3 | #include 4 | 5 | typedef struct { int x; int y; } Point; 6 | 7 | /* Ha, you thought I was gonna read these with scanf()? */ 8 | /* YOu fool. you absolute clown. I don't know how to use scanf() */ 9 | const Point points[] = {{242, 164}, {275, 358}, {244, 318}, {301, 335}, {310, 234}, {159, 270}, {82, 142}, {229, 286}, {339, 256}, {305, 358}, {224, 339}, {266, 253}, {67, 53}, {100, 143}, {64, 294}, {336, 303}, {261, 267}, {202, 86}, {273, 43}, {115, 256}, {78, 356}, {91, 234}, {114, 146}, {114, 260}, {353, 346}, {336, 283}, {312, 341}, {234, 119}, {281, 232}, {65, 203}, {95, 85}, {328, 72}, {285, 279}, {61, 123}, {225, 179}, {97, 140}, {329, 305}, {236, 337}, {277, 110}, {321, 335}, {261, 258}, {304, 190}, {41, 95}, {348, 53}, {226, 298}, {263, 187}, {106, 338}, {166, 169}, {310, 295}, {236, 191}}; 10 | 11 | /* I eyeballed that it's a 1000x1000 grid. */ 12 | #define GRID_SIZE 1000 13 | #define NUM_POINTS (sizeof(points) / sizeof(Point)) 14 | #define DISTANCE(x1,y1,x2,y2) (abs((x1) - (x2)) + abs((y1) - (y2))) 15 | 16 | /* Return the index of the closest point to (x, y), or -1 in case of a tie. */ 17 | int closest(int x, int y) { 18 | int best_index = -1; 19 | int best_distance = 99999; 20 | int tie = 0; 21 | int i; 22 | 23 | for (i = 0; i < NUM_POINTS; i++) { 24 | int distance = DISTANCE(x, y, points[i].x, points[i].y); 25 | if (distance == best_distance) { 26 | tie = 1; 27 | } else if (distance < best_distance) { 28 | best_index = i; 29 | best_distance = distance; 30 | tie = 0; 31 | } 32 | } 33 | return tie ? -1 : best_index; 34 | } 35 | 36 | int main() { 37 | int is_infinite[NUM_POINTS] = {0}; 38 | int area[NUM_POINTS] = {0}; 39 | int i, k, x, y; 40 | 41 | /* The only clever part: probe a border of points waaay outside the grid. */ 42 | /* All the points with an infinite neighborhood must still show up there! */ 43 | /* Basically, we read along this white border: https://i.imgur.com/IINYpur.png */ 44 | /* (For the Manhattan metric, a GRID_SIZE margin is more than enough.) */ 45 | const int margin = GRID_SIZE; 46 | const int lo = 0 - margin; 47 | const int hi = GRID_SIZE + margin; 48 | for (k = lo; k < hi; k++) { 49 | i = closest(lo, k); if (i >= 0) is_infinite[i] = 1; 50 | i = closest(hi, k); if (i >= 0) is_infinite[i] = 1; 51 | i = closest(k, lo); if (i >= 0) is_infinite[i] = 1; 52 | i = closest(k, hi); if (i >= 0) is_infinite[i] = 1; 53 | } 54 | 55 | /* Count areas. */ 56 | for (y = 0; y < GRID_SIZE; y++) 57 | for (x = 0; x < GRID_SIZE; x++) { 58 | i = closest(x, y); 59 | if (i >= 0 && !is_infinite[i]) 60 | area[i]++; 61 | } 62 | 63 | /* Part A: what's the largest finite area? */ 64 | { 65 | int largest_area = 0; 66 | for (i = 0; i < NUM_POINTS; i++) 67 | if (!is_infinite[i] && area[i] > largest_area) 68 | largest_area = area[i]; 69 | printf("largest_area: %d\n", largest_area); 70 | } 71 | 72 | /* Part B: how many grid points are "safe" (sum of distances < 10000)? */ 73 | { 74 | const int threshold = 10000; 75 | int safe_area = 0; 76 | for (y = 0; y < GRID_SIZE; y++) 77 | for (x = 0; x < GRID_SIZE; x++) { 78 | int sum_distance = 0; 79 | for (i = 0; i < NUM_POINTS && sum_distance < threshold; i++) 80 | sum_distance += DISTANCE(x, y, points[i].x, points[i].y); 81 | if (sum_distance < threshold) 82 | safe_area++; 83 | } 84 | printf("safe_area: %d\n", safe_area); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /day-1.go: -------------------------------------------------------------------------------- 1 | // https://adventofcode.com/2018/day/1 in Go 2 | // Try this code online: https://tio.run/#go 3 | 4 | package main 5 | import "fmt" 6 | 7 | var problem_input = [...]int {-15,-17,-16,-12,-4,+10,+10,-19,-18,-6,+4,+4,+5,-11,-4,-12,+1,-8,-10,-16,-18,+17,-16,-7,-5,+2,-4,-1,+13,-19,+12,+19,+5,+11,+1,-16,-8,+19,+15,-14,+1,-12,-17,+10,-3,+5,+7,+4,-10,+5,-7,+15,+11,+10,+19,-17,-3,+14,+8,-14,+7,+11,+16,-7,-1,+6,+4,-1,+15,+17,-10,+4,+16,-18,-10,-2,-18,-13,+15,-7,-14,-12,-2,+1,+7,+5,-1,-19,+1,+9,-20,-9,+1,+16,-7,+11,+14,-24,+4,-3,+21,-17,-10,-44,+13,+2,-9,-15,-17,-8,+13,+4,-1,+4,+1,+2,+6,-18,+1,+19,+4,+7,-15,-12,+4,+17,-3,-15,-19,-19,-17,+16,+17,-19,-3,-9,+4,+12,+12,+12,-21,+17,-19,-4,+16,-4,-18,-16,+15,+14,+12,-6,+16,-1,+16,+6,-4,+6,-4,+11,+20,-7,+12,+51,+19,+15,-2,+12,-9,-6,+1,+10,+1,+19,+8,+3,-1,-19,-2,+4,+11,+8,+2,+10,+14,+4,-8,+9,-13,+18,+5,-8,+15,-19,-13,-13,+10,-4,-16,+17,-3,+7,+20,+18,-12,+13,-10,+14,+19,+17,+7,+7,+3,+17,-8,-4,+1,-8,-12,-1,-15,+9,-7,+6,+10,+4,+4,-14,-9,-19,+13,-5,+19,-17,-1,+5,-13,+2,+4,+15,+8,+10,+8,+17,+17,+1,+6,-15,+5,+12,+11,-3,+12,-6,-17,-17,+10,-15,-5,-1,-4,-3,+5,-21,-2,-18,+3,+19,+10,+15,-5,+17,-14,+6,+7,+3,-11,+6,+20,-9,+6,+6,-10,-9,-8,+16,+19,-9,+5,+6,-3,+12,-8,+7,-1,+16,+17,-19,+3,+11,+18,+19,-16,+11,+11,+5,+11,-25,+13,-7,+9,-18,-16,+18,+22,+35,+82,+8,+11,+19,+17,-11,+19,-16,+1,+21,+17,+18,-9,-10,+8,-15,+4,+20,-19,-10,+2,+22,+11,-5,+16,-7,-7,-15,-11,+28,+13,-7,+1,+3,+11,-7,+3,-15,+34,+5,-22,+7,-15,-2,+29,-45,-51,-6,+10,+6,+22,-17,-24,-12,+38,-71,+46,-10,-19,-15,-115,-26,-17,-2,+4,-15,+9,-4,-10,-14,-4,-18,-18,+21,+8,+13,+15,-13,-6,-6,+7,+4,+12,-7,-20,-14,-22,-2,-16,-7,-2,+12,-24,-18,+35,-45,+128,-2,+22,-10,-17,+14,-4,-11,+13,-6,+29,-40,-11,+7,-25,+5,-10,+58,-171,-169,-93,-106,-61611,-19,-3,-3,+14,+1,-6,+11,-7,-16,-13,-7,-14,+12,+4,-8,-12,-2,-3,+14,-18,-10,+3,-7,-15,-3,-13,+3,+1,-15,-5,-11,+15,+14,-9,+6,+2,+13,+5,+15,-9,+5,+2,+15,+18,+16,-17,-5,-18,+1,+6,-25,-17,+5,+13,-14,+7,+9,-11,-19,-13,-15,+9,-10,+5,+7,+6,-16,-11,+1,+1,+15,-18,-9,-1,+6,-17,-5,-19,+15,+7,-15,+7,+19,-7,-10,-5,-1,+17,+15,-3,+14,+5,-7,-8,-14,-8,-23,-18,-14,+11,+1,+7,-18,+1,-5,+6,-9,-7,-19,-2,+17,-2,+8,-10,+7,-22,-14,-10,-8,+17,-19,-12,+15,+9,-16,+9,+16,-5,-18,-4,-7,-4,-17,+14,+1,+8,-10,+3,-19,+7,-11,+14,+12,+12,-4,-19,-10,-8,-1,-16,-2,-7,-15,-5,-2,-15,+11,-17,-1,-5,+1,-9,-3,-13,-14,+8,-17,-4,-4,-12,+3,+1,+19,+2,+2,+5,-2,-9,+19,+17,-2,-19,+13,+12,+16,+10,-11,+12,-5,+10,+5,-7,+18,+11,+1,-15,+6,-10,+17,-19,+18,+8,+11,-2,+19,-21,-18,-18,-15,+4,+18,+13,-4,+14,+19,-7,+9,+7,+21,+18,-4,+18,+15,-6,+16,+8,-12,+3,-18,+4,+20,-8,-9,+15,+12,+1,+17,+6,-9,+13,-12,-13,+10,-6,-1,+17,+12,-18,+4,+5,+11,-6,+1,+14,-17,+4,+17,+9,-15,-8,+7,+5,-7,+14,+5,+13,+7,-1,-7,-10,+14,-6,+26,+15,+1,+13,+13,-11,-6,+16,-2,-19,+8,-14,+7,-16,+21,+17,+3,+21,+4,-8,-2,+28,+1,-17,+14,-9,+2,-18,+12,+10,+18,-17,-15,-18,+2,+1,-31,+8,-20,-2,-20,-21,+9,+8,+10,-45,+20,+27,+10,-65,+12,-17,-9,-11,+24,+7,-46,-13,+12,-19,+1,-25,+13,-5,-19,+9,-14,-6,+14,+4,-22,-5,-2,-7,+34,+13,-17,-8,+19,+22,-13,+6,+6,+6,-16,+25,+12,+8,-16,-32,-12,-36,-21,+1,+13,-17,-11,+9,+13,+7,-11,-13,-10,-14,+18,+4,-25,+4,-12,+6,-20,-10,+17,-11,+3,-10,-4,-10,+9,-15,-6,+17,+13,-28,-8,+4,-20,+13,-6,+12,-17,-9,-14,+13,-4,+57,-5,+37,-14,+6,-15,-16,-2,-31,+3,+88,+8,-22,+6,+22,+17,+9,-31,-121,-45,+21,-82,-37,-93,-31,-17,+117,-61928,+11,-17,-10,+9,+10,-18,+10,-7,-6,-5,-4,+19,-12,+17,+9,-16,+1,+10,-12,-6,+19,-4,-11,-15,-7,+13,-10,+2,-15,+2,-8,+17,+9,+4,-8,-19,+7,-8,-12,-1,-5,+2,+6,-15,+8,+11,+13,-18,-8,-11,-16,+13,+6,-18,-10,+19,+2,-8,-10,-13,-19,+15,+8,-5,+19,+3,+12,-16,-5,-5,-18,-11,-7,+4,-5,+15,-1,+10,-6,-5,-16,+9,+10,+16,+14,-15,+10,-18,-11,-13,-11,-2,-9,-17,+11,+8,+125143} 8 | 9 | func main() { 10 | var sum = 0 11 | for _, x := range problem_input { 12 | sum += x 13 | } 14 | fmt.Println(sum) 15 | 16 | var acc = 0 17 | var seen = make(map[int]bool) 18 | for { 19 | for _, x := range problem_input { 20 | if (seen[acc]) { fmt.Println(acc); return; } 21 | seen[acc] = true 22 | acc += x 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /day-7.cob: -------------------------------------------------------------------------------- 1 | 000010 IDENTIFICATION DIVISION. 2 | 000020 PROGRAM-ID. ADVENTOFCODE7. 3 | 000030 4 | 000040 DATA DIVISION. 5 | 000050 WORKING-STORAGE SECTION. 6 | 000060 7 | 000070 01 INPUT-LINE PIC X(99). 8 | 000080 01 INPUT-FROM PIC A. 9 | 000090 01 INPUT-TO PIC A. 10 | 000100 11 | 000110 01 D PIC 999. 12 | 000120 01 DEPENDENCY OCCURS 200 TIMES. 13 | 000130 05 D-FROM PIC A. 14 | 000140 05 D-TO PIC A. 15 | 000150 01 NUM-DEPS PIC 999. 16 | 000160 17 | 000170 01 T PIC 999. 18 | 000180 01 NUM-TASKS PIC 99. 19 | 000190 01 COMPLETED-RECORD. 20 | 000200 05 COMPLETED PIC 9 OCCURS 26 TIMES. 21 | 000210 22 | 000220 01 IS-DOABLE PIC 9. 23 | 000230 01 ALL-DONE PIC 9. 24 | 000240 01 JF PIC 99. 25 | 000250 01 JT PIC 99. 26 | 000260 01 THE-ALPHABET PIC A(26) VALUE "ABCDEFGHIJKLMNOPQRSTUVWXYZ". 27 | 000270 28 | 000280 01 SECOND PIC 9999. 29 | 000290 01 NUM-WORKERS PIC 99 VALUE 5. 30 | 000300 01 W PIC 9. 31 | 000310 01 WORKERS. 32 | 000320 05 WORKER OCCURS 5 TIMES. 33 | 000330 10 WORK-ON PIC A. 34 | 000340 10 WORK-LEFT PIC 99. 35 | 000350 01 WORK-LEFT PIC 99 OCCURS 5 TIMES. 36 | 000360 01 PROGRESS-RECORD. 37 | 000370 05 IN-PROGRESS PIC 9 OCCURS 26 TIMES. 38 | 000380 39 | 000390 40 | 000400 PROCEDURE DIVISION. 41 | 000410 42 | 000420* READ THE INPUT. 43 | 000430 SET D TO 0 44 | 000440 PERFORM FOREVER 45 | 000450 ACCEPT INPUT-LINE 46 | 000460 IF INPUT-LINE = SPACE THEN EXIT PERFORM END-IF 47 | 000470 ADD 1 TO D 48 | 000480 MOVE INPUT-LINE(6:1) TO D-FROM OF DEPENDENCY(D) 49 | 000490 MOVE INPUT-LINE(37:1) TO D-TO OF DEPENDENCY(D) 50 | 000500 END-PERFORM 51 | 000510 MOVE D TO NUM-DEPS 52 | 000520 53 | 000530* COUNT HOW MANY TASKS THERE ARE. 54 | 000540 MOVE 0 TO NUM-TASKS 55 | 000550 PERFORM WITH TEST AFTER VARYING D FROM 1 BY 1 UNTIL D = NUM-DEPS 56 | 000560 PERFORM COMPUTE-J 57 | 000570 MOVE FUNCTION MAX(NUM-TASKS, JF, JT) TO NUM-TASKS 58 | 000580 END-PERFORM 59 | 000590 60 | 000600* PART 1. 61 | 000610 PERFORM PRINT-NEXT-TASK WITH TEST AFTER UNTIL ALL-DONE = 1 62 | 000620 DISPLAY " ". 63 | 000630 64 | 000640* PART 2. 65 | 000650 INITIALIZE COMPLETED-RECORD. 66 | 000660 INITIALIZE PROGRESS-RECORD. 67 | 000670 MOVE 0 TO SECOND 68 | 000680 PERFORM DO-WORK WITH TEST AFTER UNTIL ALL-DONE = 1 69 | 000710 STOP RUN. 70 | 000720 71 | 000730 72 | 000740 PRINT-NEXT-TASK SECTION. 73 | 000750 MOVE 1 TO ALL-DONE 74 | 000760 PERFORM WITH TEST AFTER 75 | 000770 VARYING T FROM 1 BY 1 UNTIL T = NUM-TASKS 76 | 000780 IF COMPLETED(T) = 0 THEN 77 | 000790 MOVE 0 TO ALL-DONE 78 | 000800 MOVE 1 TO IS-DOABLE 79 | 000810 PERFORM CHECK-DOABLE 80 | 000820 IF IS-DOABLE = 1 THEN 81 | 000830 DISPLAY THE-ALPHABET(T:1) WITH NO ADVANCING 82 | 000840 MOVE 1 TO COMPLETED(T) 83 | 000850 EXIT PERFORM 84 | 000860 END-IF 85 | 000870 END-IF 86 | 000880 END-PERFORM 87 | 000890 EXIT SECTION. 88 | 000900 89 | 000910 COMPUTE-J SECTION. 90 | 000920 COMPUTE JF = FUNCTION ORD(D-FROM OF DEPENDENCY(D)) - 65 91 | 000930 COMPUTE JT = FUNCTION ORD(D-TO OF DEPENDENCY(D)) - 65 92 | 000940 EXIT SECTION. 93 | 000950 94 | 000960 CHECK-DOABLE SECTION. 95 | 000970 PERFORM WITH TEST AFTER 96 | 000980 VARYING D FROM 1 BY 1 UNTIL D = NUM-DEPS 97 | 000990 PERFORM COMPUTE-J 98 | 001000 IF (T = JT) AND COMPLETED(JF) = 0 THEN 99 | 001010 MOVE 0 TO IS-DOABLE 100 | 001020 END-IF 101 | 001030 END-PERFORM 102 | 001040 EXIT SECTION. 103 | 001050 104 | 001060 DO-WORK SECTION. 105 | 001070 PERFORM WORK-STEP 106 | 001080 WITH TEST AFTER VARYING W FROM 1 BY 1 107 | 001090 UNTIL W = NUM-WORKERS 108 | 001100 109 | 001110 MOVE 1 TO ALL-DONE 110 | 001120 PERFORM WITH TEST AFTER 111 | 001130 VARYING T FROM 1 BY 1 UNTIL T = NUM-TASKS 112 | 001140 IF COMPLETED(T) = 0 THEN 113 | 001150 MOVE 0 TO ALL-DONE 114 | 001160 END-IF 115 | 001170 IF IN-PROGRESS(T) = 0 THEN 116 | 001180 MOVE 1 TO IS-DOABLE 117 | 001190 PERFORM CHECK-DOABLE 118 | 001200 IF IS-DOABLE = 1 THEN 119 | 001210 PERFORM ASSIGN-TASK 120 | 001220 END-IF 121 | 001230 END-IF 122 | 001240 END-PERFORM 123 | 001250 124 | 001260 DISPLAY SECOND, " ", WORKER(1) 125 | 001270 , " ", WORKER(2) 126 | 001280 , " ", WORKER(3) 127 | 001290 , " ", WORKER(4) 128 | 001300 , " ", WORKER(5) 129 | 001310 ADD 1 TO SECOND 130 | 001320 EXIT SECTION. 131 | 001330 132 | 001340 WORK-STEP SECTION. 133 | 001350 IF WORK-LEFT OF WORKER(W) > 0 THEN 134 | 001360 SUBTRACT 1 FROM WORK-LEFT OF WORKER(W) 135 | 001370 IF WORK-LEFT OF WORKER(W) = 0 THEN 136 | 001380 COMPUTE T = FUNCTION ORD(WORK-ON OF WORKER(W)) - 65 137 | 001390 MOVE SPACE TO WORK-ON OF WORKER(W) 138 | 001400 MOVE 1 TO COMPLETED(T) 139 | 001410 END-IF 140 | 001420 END-IF 141 | 001430 EXIT SECTION. 142 | 001440 143 | 001450 ASSIGN-TASK SECTION. 144 | 001460 PERFORM 145 | 001470 WITH TEST AFTER VARYING W FROM 1 BY 1 146 | 001480 UNTIL W = NUM-WORKERS 147 | 001490 148 | 001500 IF WORK-LEFT OF WORKER(W) = 0 149 | 001510 MOVE THE-ALPHABET(T:1) TO WORK-ON OF WORKER(W) 150 | 001520 MOVE T TO WORK-LEFT OF WORKER(W) 151 | 001530 ADD 60 TO WORK-LEFT OF WORKER(W) 152 | 001540 MOVE 1 TO IN-PROGRESS(T) 153 | 001550 EXIT SECTION 154 | 001560 END-IF 155 | 001570 END-PERFORM 156 | 001580 EXIT SECTION. 157 | 001590 158 | -------------------------------------------------------------------------------- /day-2.pl: -------------------------------------------------------------------------------- 1 | % https://adventofcode.com/2018/day/2 in Prolog (SWI). 2 | % Try it here: https://tio.run/#prolog-swi 3 | 4 | problem_input(["kbqwtcvzgumhpwelrnaxydpfuj","kbqwtcvzgsmhpoelryaxydiqij","kbqwpcvzssmhpoelgnaxydifuj","kbqgtcvxgsmhpoalrnaxydifuj","kbqwtcvygsmhpoelrnaxydiaut","kbqwtcvjgsmhpoelrnawydzfuj","kbqftcvzgsmhpoeprnaxydifus","rbqwtcgzgsxhpoelrnaxydifuj","kbqwtlvzgvmhpoelrnaxkdifuj","kbqwtcvzgsmhpolqrnaxydifub","kbqbtcqzgsmhposlrnaxydifuj","kbqwmcvzgswhpoelxnaxydifuj","kbqwtyvzgsmhkoelrnsxydifuj","khqwtcvzgsmhqoelinaxydifuj","koqwtcvzcsmhpoelrnaxydizuj","kbqwtcvzlsmhpoezrnaxydmfuj","kbqwtcvzdsmhpoelrjaxydifij","kbqwtcvzgsmhpoelrncxyjifuk","kbtwtcvzgsmhpoelonaxydiwuj","kbqwfcrzgsmhpoelrnaeydifuj","kbqutcvkgsmhpoelrnfxydifuj","kbqwtcvzgsmvvoelrnaxydihuj","kbqwtcvzhymhpoelrnaxydifyb","kbqctcvzgumhpoalrnaxydifuj","kuqktcvzgsmhpoelrnaxydieuj","kbqwtcvzgsmvpozlrnaxydifmj","kbqwtcvzgsmhpojlraaxydiouj","kbqwtcvzgmmhpoelknaxydizuj","kbwwtcvzgsmhpoefrnaxydifij","kbqwucvzgsmhpoelvnahydifuj","kbqwtcvzpsmhpgelrqaxydifuj","kblqtcvzgsmhpoeirnaxydifuj","kbqwtcvzgsmhpovlrnabydifum","kbqwwcvzgsmhpoelrnaoydnfuj","kyqwdcvzgsmhpoelrnaxfdifuj","kbqftcvzgsmxpoelknaxydifuj","kbqwtsvzksmhpoelqnaxydifuj","kbqwtcvzgsmhplelrnauydifux","kbqytcvzgsmhpkelrnaxydefuj","kbqwtcvzgsmjjoelrlaxydifuj","kbqvtcvzgsmhpoelnnaxydafuj","kbqwtcvzgsjhioelrnaxpdifuj","kbqptcvpgsmhpoelrnaxydiful","kbqwjcazgimhpoelrnaxydifuj","kbqxtcvzgwmhpaelrnaxydifuj","kbqwtcezgsmhqoelrnaxydifub","kbqwtcvzgsmhooelynaxydifuf","kbqwtwvzgsmkpoelrnaxrdifuj","nbqwtcvugsmhpoelrnzxydifuj","kbvwqcvzgsmhpoelsnaxydifuj","kbqwtcyzjsmhpoelrnaxymifuj","kbqwtcvzgsmhpoclrnaxykzfuj","kbbwtcvzgsmhyodlrnaxydifuj","kbwwtcvzgsmytoelrnaxydifuj","kbmwtcczgpmhpoelrnaxydifuj","ubqwtcvzgsmmpoblrnaxydifuj","kbqwtcvzgrmhpoelrnaxnrifuj","kbqwhcvzgsmhpoelynaaydifuj","kbqwtcvzgsmtpoelrcpxydifuj","kdqwtchzgsmhpoelrmaxydifuj","qbqrncvzgsmhpoelrnaxydifuj","kbqwtcvzghshpoelrnaxodifuj","kbqwhcvzgsmhpoelknaxydiwuj","ebqwtcvzgsmhpoelrotxydifuj","kbqwacvzusmhpoelryaxydifuj","kbqwtcvggsmhpoelrnaxygifyj","kbqwtcvzgsmhpoelrnaxycwfuo","kzqwzcvzgsmhpoelrxaxydifuj","khqwtcvzgsmhpoelrnaxldifyj","kbqwtbtzgsmhpoelrnaxydifud","gbqwtcvzgqmhpoelrnaxydifrj","kbqdtqvzgwmhpoelrnaxydifuj","kbqwscvzgsmhpoelrpaxypifuj","kmqwtcdzgsmhpoelenaxydifuj","klqwtcvvgsmhpoelrfaxydifuj","kbuwtcvzgsmhpoelrtaxyuifuj","kbqwtcvrgomhpoelrnaxydijuj","kbqwtgvzgsmhzoelrnpxydifuj","kbqltcvzgsmhooeljnaxydifuj","kbqwtcvzgbmxpoelrnaxydivuj","kbqdtcmzgsmhpoelrnaxydmfuj","kbqwtcazgsmhpoplrnacydifuj","kbqztcvegsmhpoelrnvxydifuj","kbqwtcvzgsmhpoecrnaxydzfsj","kbqwtcvzgsmepoelrnaqydifuf","kbqwtcqzgsmhpoelrnoxydivuj","kbqwtcvzgsmhpoeylnaxydhfuj","kbqwtcvfgsmhpoelrnaxgdifyj","kbqwtcvzgsmhnbelrnaxyfifuj","kbqwtcvzgsmhpoelrnaxbdffmj","kwqwtcvogtmhpoelrnaxydifuj","kdqwtcvzggyhpoelrnaxydifuj","kbqwtuvzgtmhpoelrnaxydifxj","kbqctdvzcsmhpoelrnaxydifuj","kbqwtcvzgsmhpoblrniyydifuj","kbqwucvzzsmhpoelrnvxydifuj","kbqwtcvzgslzpoelrnaxydiruj","kbqwtdmzgsmhpwelrnaxydifuj","kbqwtcvzgsmhpoilrnaxqiifuj","kbqwtcvzgsmhpgelrnaxydisnj","kbdwtqvzgsmhpoelrnaxydivuj","kbqvtdvzgsmhpoelrjaxydifuj","kfqwtcvzgsmhpoeurnyxydifuj","kbqwtcvzgsmhpoglrnaxqkifuj","kbqwtcvrgsmhpoelrnajydifnj","xbqwpcvzgjmhpoelrnaxydifuj","kbqwtcvzgsmhpoelrdaxvdihuj","kbuwtcvzssmhpoklrnaxydifuj","kbqwtcvzgqmhpoelrnzxydifbj","kbqwtcvzgsmhsoeoknaxydifuj","kfqltcvzgsmhpoelrnaxydifnj","qbqwtsvzgsmhpoelrnaxodifuj","kbqwwevzgsmypoelrnaxydifuj","kbqwtcuzgimhpoelrnaxydffuj","kxqwlcvzgsmhpoelrnaxyrifuj","nbqwtcvzgsmhpoelryaxyiifuj","kbqwtcvzgsmhhoxlreaxydifuj","mbqwtcvzfsmxpoelrnaxydifuj","kbqwttvzgsmhpoeqrnaxidifuj","kbqwtcvzgamhpielrnaxyiifuj","rfqwtcvzgsmhpoelrnaxydifun","kbpwtqvzgsmbpoelrnaxydifuj","kbqwtcvzgsmhpoqlroaxydifua","hbqwtcvzksmhpoelrnaxydbfuj","kaqutcvzgsmhpoelrnaxydiiuj","kbqctcvzgsnhpoelrcaxydifuj","kbqwtnvzgsmhpoelrnaxydqfoj","kbqwtcvzhsmhpoelrnaxydifyb","ubqwtcvcgsmhooelrnaxydifuj","kbqwtcvrgsmhpoelrnaxtdivuj","kbqwtcvzgsmhplelrnmxydifaj","ebqwlcvzghmhpoelrnaxydifuj","hbqwtcvzgsmhpoelrnaqyeifuj","kbqstcvzgsmeprelrnaxydifuj","kbqwtcvogsthpoelrnnxydifuj","ybqwtcvzgdmhpoelrnaxydufuj","kbqutcvzgsmhpoelrnaxydifgx","kbqwtcvzgsmhpozlunadydifuj","kkqwtcvzgsmhpuefrnaxydifuj","kbqrtcvzgsmhpoelrnaxcdifuq","kbqwtcvzjsmupoelrnaxydiluj","kbqwmcvzgsuhpoelrnaxydifhj","kbqwfcvzgsmhpoelrnaxydkzuj","kbqatcvzgsdhpoeyrnaxydifuj","kbtwtcvzusmhpoelrxaxydifuj","kbqwtcwzgsmhpoelrnaxysofuj","kbqqtcvmgsmhpoevrnaxydifuj","kbqwjcvzgsmhpoelrnaxydhuuj","mbdwtcvzgsmhpoelqnaxydifuj","kbqwtcvlgsmhpoelrdaxydifaj","kbqwtcvzgsmmpoelrlaxydnfuj","kbqwtchfggmhpoelrnaxydifuj","kbqqtcvzgsyhpoelrnaxyoifuj","knqwtcvzqsmupoelrnaxydifuj","kdqdtcvzgsmhpoelrnaxydmfuj","kbqwtcvzgsmhptelrnawyhifuj","kbqwtcvzgrmhpoeqrnaxydifuw","kbnxtcvzgsmhpoelrnauydifuj","kbqwacvsgsmhpoelrnaxydifgj","kbqwtcvzgsmhpperrnaxydifuc","gbqwtcvzgsqhxoelrnaxydifuj","kbqwtcvzgsmhpoeljgaxydifwj","kbqktcvzgsmhpotlrnatydifuj","bbqwtcvzgsmhpoilrnaxydjfuj","kbqwecvdgsmhpoelrnaxypifuj","keqwtcvzgemhpotlrnaxydifuj","kbqptcvzgsmvpoelrnaxydixuj","kbqwbctzgsmhpoelrnaxydifup","kbqwtcvzgszhpbelrnzxydifuj","mbqwtcvtgsmhpoeyrnaxydifuj","kbqwtcvzgsmhqcelrhaxydifuj","kbqotcvzgsmhooelrnazydifuj","kbqwtcvzgsmhpoelmpaxyiifuj","kbqwtcvwgsmypoclrnaxydifuj","kbqwtcvsgskhpoelrnaxykifuj","kbqwtcvzgszvpoelrnwxydifuj","kbqwtcvzgsmhpoejonaxydrfuj","kbqwtcvzgsmhkoelrnazyqifuj","kbzwtzvzgsmhptelrnaxydifuj","kbqwtcdzgsmhptelrnaxydiduj","kbqwtcvzgamhpoelrnakyzifuj","kbqwtcvzgsmhpoeonnaxydifxj","kbqwtcvzgsmhpoeranaxydifej","kbqwscvzgsmhpoelunaxydimuj","cbqwtcvzgsmhpoelrdaxydefuj","vbqwtcjzgsmhpoelrnaxydifua","kmqwtcvzksmhpoeljnaxydifuj","kbqwtcvzgsmppojlrnasydifuj","kaqwtcvfgsmhpoelrnaxydiauj","khqwccvzgsmhpoelrnaxydifud","vbqwtcvzrsmhpoelrhaxydifuj","kuqwtcvzgsmhpoelgnaiydifuj","kbqwtcvzdsmhpbelvnaxydifuj","kbowtcvzgnmhpoelrfaxydifuj","kbqwtcvsgsmhfoejrnaxydifuj","kbqwtcvzgskhtoelrnxxydifuj","kbqwtcvzgtmhpoevrnaxydivuj","bbqptcgzgsmhpoelrnaxydifuj","kbqwtpvzgsmnpoelhnaxydifuj","kbqwtovzgsmmpoelrnaxydifuw","kbqwtcvzgsihpwelrnaxydsfuj","kbqwtcvzggmhpollrnaxydifsj","kbqwtcjzgsmhpoelrnaxyxifub","ebqwtcvzgsmzpoelrnaaydifuj","kbqwtcvzusmhpoelrnqxydijuj","obqwtcvzgsghpoelrnaxydifkj","kbrwtcvzmdmhpoelrnaxydifuj","kbqwtcvzxsmhpoblrnhxydifuj","kbqwacvzgsahpoelrnaxydiguj","kyqwtcvzgsmipoelrnlxydifuj","kbbwtcvzgsmhboelpnaxydifuj","kbqwtcvzgsmhpoelrnaxhdosuj","kbqwtgvzgxmhpoelrnaxyrifuj","pbqwtsvzgsmhpoelrnabydifuj","kbqrtcvzgsmhpsblrnaxydifuj","kbqwtcvzgsmhpoexrnaaycifuj","kbqxtcvzgsjhkoelrnaxydifuj","kbqwtcvzgsmhpxelrnaxydifby","lbxwtcvzgsmdpoelrnaxydifuj","kbqwtcczgsmhpoklrnzxydifuj","zbqwtcvzgsmhpoelrbaxydifui","krqwtcvzbsmhpoelrjaxydifuj","kbkwtcvzgsmhpoelrnaxydiacj","kbqwtcvzgszhpseprnaxydifuj","kbxwtcvzxsmhpoesrnaxydifuj","kbqwdcvzgsmhpoelrbaxygifuj","kbqwthkzgsmhhoelrnaxydifuj","klqwtchzgamhpoelrnaxydifuj","obqwtcvzgsvcpoelrnaxydifuj","kblwtcvzgsmhpoelrnanydifuw","kbqwtrvzgsmhpoelynaxydifug","kbqwtcvzgsmhcoelmnaxydkfuj","kbqwtcvzgsmhpotlqoaxydifuj","kaqatcvzgsmhpoelrnaxyiifuj","kbqttcvwgsmhpoelrnaxydifgj","kpqwtcvzgsmhpwelynaxydifuj","kbqwucvzgsmhpyelrnaxyxifuj","kbqwucvzgsmhprelrnaxyfifuj","kbqwthvzgsmhphelrnaxylifuj","kbqwtcvzosmhdoelrnaxwdifuj","kbqwtxvsgsphpoelrnaxydifuj","koqwtcvfghmhpoelrnaxydifuj","kbtwicvzpsmhpoelrnaxydifuj","kbawtcvzgsmhmoelrnaxyiifuj","kbqwtcvzgslhpbelrnaxydifuk","kbqttcvzgsmypoelrnaxydifua","kbqwtcvrgqmhpnelrnaxydifuj","kbqwtcvzghmhpoekpnaxydifuj","kbqwtcvzgsmupoelrnaxidifui","kbqwtcvzgsmhpbelrnaxrdifux"]). 5 | 6 | %%%% Part A 7 | 8 | % X occurs N times in Xs. 9 | count(N, X, Xs) :- include(=(X), Xs, Xs1), length(Xs1, N). 10 | 11 | % Xs has any member that occurs N times. 12 | has(N, Xs) :- member(X, Xs), count(N, X, Xs), !. 13 | 14 | ?- problem_input(P), 15 | maplist(string_chars, P, Pc), 16 | aggregate_all(count, (member(X, Pc), has(2, X)), N2), 17 | aggregate_all(count, (member(X, Pc), has(3, X)), N3), 18 | Checksum is N2 * N3, 19 | write(Checksum), nl. 20 | 21 | %%%% Part B 22 | 23 | % Lists differ in only one place. 24 | differ_by_one([H|T], [K|T]) :- H \= K. 25 | differ_by_one([H|T], [H|U]) :- differ_by_one(T, U). 26 | 27 | % common("abc3de", "abc4de", "abcde") holds, for example. 28 | common([], [], []). 29 | common([H|Tx], [H|Ty], [H|Tc]) :- common(Tx, Ty, Tc). 30 | common([Hx|Tx], [Hy|Ty], Tc) :- Hx \= Hy, common(Tx, Ty, Tc). 31 | 32 | ?- problem_input(P), 33 | maplist(string_chars, P, Pc), 34 | member(X, Pc), 35 | member(Y, Pc), 36 | differ_by_one(X, Y), 37 | common(X, Y, C), 38 | string_chars(S, C), 39 | write(S), nl. 40 | -------------------------------------------------------------------------------- /day-16.idr: -------------------------------------------------------------------------------- 1 | -- https://adventofcode.com/2018/day/16 in Idris. 2 | import Data.Bits as B 3 | import Data.Fin 4 | import Data.Vect 5 | 6 | -- On HARD MODE. 7 | %default total 8 | 9 | -- This is how you know it's going to be "fun" code: 10 | ℤ : Type 11 | ℤ = Integer 12 | 13 | -- Let's describe our machine a little. It has about yea many registers: 14 | numRegs : Nat 15 | numRegs = 4 16 | 17 | -- Said registers are 0, 1, 2, and 3. Hey, I know what that type is called!! 18 | Register : Type 19 | Register = Fin numRegs 20 | 21 | -- Their values are represented by a 4-tuple of integers, then. 22 | RegisterValues : Type 23 | RegisterValues = Vect numRegs ℤ 24 | 25 | -- Oh, it can do these tricks: 26 | bAnd : ℤ -> ℤ -> ℤ 27 | bAnd x y = bitsToInt (intToBits {n=32} x `B.and` intToBits y) 28 | 29 | bOr : ℤ -> ℤ -> ℤ 30 | bOr x y = bitsToInt (intToBits {n=32} x `B.or` intToBits y) 31 | 32 | -- And these: 33 | gtZ : ℤ -> ℤ -> ℤ 34 | gtZ a b = if a > b then 1 else 0 35 | 36 | eqZ : ℤ -> ℤ -> ℤ 37 | eqZ a b = if a == b then 1 else 0 38 | 39 | -- We'll also want to do this at some point: 40 | intToFin : (n : Nat) -> ℤ -> Maybe (Fin n) 41 | intToFin n z = natToFin (fromIntegerNat z) n 42 | 43 | ----------------------------------------------------------------- 44 | 45 | -- Anyway, here are the opcodes. 46 | data Opcode = Addr | Addi | Mulr | Muli | Banr | Bani | Borr | Bori 47 | | Setr | Seti | Gtir | Gtri | Gtrr | Eqir | Eqri | Eqrr 48 | 49 | -- All of them in a list: 50 | allOpcodes : List Opcode 51 | allOpcodes = [Addr, Addi, Mulr, Muli, Banr, Bani, Borr, Bori, 52 | Setr, Seti, Gtir, Gtri, Gtrr, Eqir, Eqri, Eqrr] 53 | 54 | -- Idris doesn't have "deriving", which is a little sad, and means we have to write this: 55 | implementation Eq Opcode where 56 | Addr == Addr = True 57 | Addi == Addi = True 58 | Mulr == Mulr = True 59 | Muli == Muli = True 60 | Banr == Banr = True 61 | Bani == Bani = True 62 | Borr == Borr = True 63 | Bori == Bori = True 64 | Setr == Setr = True 65 | Seti == Seti = True 66 | Gtir == Gtir = True 67 | Gtri == Gtri = True 68 | Gtrr == Gtrr = True 69 | Eqir == Eqir = True 70 | Eqri == Eqri = True 71 | Eqrr == Eqrr = True 72 | _ == _ = False 73 | 74 | -- Whew. Anyway, here's how we represent instructions in their most executable form. 75 | data Source = Imm ℤ | Reg Register -- Where an operand comes from. 76 | data Instruction = Inst (ℤ -> ℤ -> ℤ) Register Source Source 77 | -- ↑ what to do ↑ where to put the outcome ↑ where to get the operands 78 | 79 | -- For example: Inst (+) 2 (Reg 1) (Imm 5) is an instruction to perform `reg[2] ← reg[1] + 5`. 80 | 81 | -- We run them like so (it's pretty straightforward). 82 | execute : Instruction -> RegisterValues -> RegisterValues 83 | execute (Inst f j srcA srcB) rvals = 84 | replaceAt j result rvals 85 | where grab : Source -> ℤ 86 | grab (Imm i) = i 87 | grab (Reg i) = index i rvals 88 | result = f (grab srcA) (grab srcB) 89 | 90 | -- Now, to actually map Opcode × (ℤ, ℤ, Register) to those high-level Instructions! 91 | 92 | -- We have some helper functions of type (ℤ -> Maybe Source). This one will try to 93 | -- map a number like 3 into a Source like `Just (Reg (the Register 3))`. But, for 94 | -- example, `reg' 7` will yield `Nothing`, because there's no register 7. 95 | reg' : ℤ -> Maybe Source 96 | reg' = map Reg . intToFin numRegs 97 | 98 | -- Pretty much any number can be an immediate argument, though. 99 | imm' : ℤ -> Maybe Source 100 | imm' = map Imm . Just 101 | 102 | -- The translation is achieved by this juicy big table: 103 | toInstruction : Opcode -> (ℤ, ℤ, Register) -> Maybe Instruction 104 | toInstruction Addr (a, b, c) = Inst (+) c <$> reg' a <*> reg' b 105 | toInstruction Addi (a, b, c) = Inst (+) c <$> reg' a <*> imm' b 106 | toInstruction Mulr (a, b, c) = Inst (*) c <$> reg' a <*> reg' b 107 | toInstruction Muli (a, b, c) = Inst (*) c <$> reg' a <*> imm' b 108 | toInstruction Banr (a, b, c) = Inst bAnd c <$> reg' a <*> reg' b 109 | toInstruction Bani (a, b, c) = Inst bAnd c <$> reg' a <*> imm' b 110 | toInstruction Borr (a, b, c) = Inst bOr c <$> reg' a <*> reg' b 111 | toInstruction Bori (a, b, c) = Inst bOr c <$> reg' a <*> imm' b 112 | toInstruction Setr (a, b, c) = Inst const c <$> reg' a <*> imm' b 113 | toInstruction Seti (a, b, c) = Inst const c <$> imm' a <*> imm' b 114 | toInstruction Gtir (a, b, c) = Inst gtZ c <$> imm' a <*> reg' b 115 | toInstruction Gtri (a, b, c) = Inst gtZ c <$> reg' a <*> imm' b 116 | toInstruction Gtrr (a, b, c) = Inst gtZ c <$> reg' a <*> reg' b 117 | toInstruction Eqir (a, b, c) = Inst eqZ c <$> imm' a <*> reg' b 118 | toInstruction Eqri (a, b, c) = Inst eqZ c <$> reg' a <*> imm' b 119 | toInstruction Eqrr (a, b, c) = Inst eqZ c <$> reg' a <*> reg' b 120 | 121 | ----------------------------------------------------------------- 122 | 123 | -- Okay. This task involves us piecing together how `Fin 16` maps to our opcodes. 124 | -- Let's describe how that goes: 125 | 126 | -- An instruction we haven't yet decoded. What could it be??? 127 | data MysteryInstruction = Mystery (Fin 16) (ℤ, ℤ, Register) 128 | 129 | -- A sample of a mystery instruction being applied to some registers. 130 | record Sample where 131 | constructor MkSample 132 | before : RegisterValues 133 | instruction : MysteryInstruction 134 | after : RegisterValues 135 | 136 | -- Get the instruction number out of a sample. Handy. 137 | instructionNumber : Sample -> Fin 16 138 | instructionNumber s = 139 | let Mystery n _ = instruction s in n 140 | -- (FACT: Under the Curry-Howard isomorphism, this function is proof 141 | -- that I should have probably made `Mystery` a record) 142 | 143 | -- Given a sample, return a list of opcodes it *might* be. 144 | candidates : Sample -> List Opcode 145 | candidates (MkSample before (Mystery _ abc) after) = 146 | filter fitsSample allOpcodes 147 | where 148 | fitsSample op = 149 | case toInstruction op abc of -- Try to decode it as `op`... 150 | Nothing => False -- If that *fails* (pffft), then `op` definitely isn't right. 151 | Just i => execute i before == after -- Execute `i` and check its work, otherwise. 152 | 153 | -- This doesn't fully narrow it down, so it's time to solve SUDOKUS (sort of). 154 | 155 | ----------------------------------------------------------------- 156 | 157 | -- Whittle a mutually exclusive list of candidate lists down to a single assignment. 158 | -- For example, `sudoku [[a,b,c],[b],[a,b]] == Just [c,b,a]`. 159 | -- (The middle one is b, so we scrap that from the others, and now we've isolated a... etc.) 160 | sudoku : Eq a => {n : Nat} -> Vect n (List a) -> Maybe (Vect n a) 161 | sudoku = solutionHopefully . untilEquality whittle 162 | where 163 | -- Okay, you got me. This isn't actually total, but for our purposes it is. 164 | -- (The boxes can only lose elements, so `untilEquality whittle` must terminate.) 165 | untilEquality : Eq b => (b -> b) -> b -> b 166 | untilEquality = 167 | assert_total $ \f, x => 168 | let x' = f x in 169 | if x == x' then x else untilEquality f x' 170 | 171 | -- Remove all isolated cell values from all not-yet-isolated cells. 172 | whittle : Eq a => Vect n (List a) -> Vect n (List a) 173 | whittle boxes = map narrowDown boxes 174 | where 175 | isolated : List a -- Gather all the values we have on lockdown so far. 176 | isolated = concat $ filter ((==1) . length) $ toList boxes 177 | 178 | unisolated : a -> Bool 179 | unisolated y = not (elem y isolated) 180 | 181 | narrowDown : List a -> List a 182 | narrowDown x = if length x == 1 then x else filter unisolated x 183 | 184 | -- Return the only element in a list. 185 | only : List a -> Maybe a 186 | only [x] = Just x 187 | only _ = Nothing 188 | 189 | -- Return the unique solution, praying there is one. 190 | solutionHopefully : Vect n (List a) -> Maybe (Vect n a) 191 | solutionHopefully = sequence . map only 192 | 193 | -- (For example, `sudoku [[1,2],[1,2,3]]` stops early, at `[[1,2],[1,2]]`. 194 | -- Then, `only` will return some `Nothing`s, and the whole `sequence` will fail.) 195 | 196 | ----------------------------------------------------------------- 197 | 198 | -- Anyway, now we can start to figure out the whole mapping: 199 | OpcodeMapping : Type 200 | OpcodeMapping = Vect 16 Opcode 201 | 202 | -- From a list of samples, try to deduce a mapping from [0..15] to Opcodes. 203 | deduceMapping : List Sample -> Maybe OpcodeMapping 204 | deduceMapping samples = sudoku (map candidateOpcodes range) 205 | where 206 | candidateOpcodes : Fin 16 -> List Opcode 207 | candidateOpcodes i = 208 | let relevantSamples = filter ((== i) . instructionNumber) samples 209 | in [op | op <- allOpcodes, all (elem op . candidates) relevantSamples] 210 | 211 | -- Decode a mystery instruction using a given opcode mapping. 212 | -- (This should always return a "Just" result, if the supplied mapping is correct.) 213 | reveal : OpcodeMapping -> MysteryInstruction -> Maybe Instruction 214 | reveal v (Mystery i abc) = toInstruction (index i v) abc 215 | 216 | ----------------------------------------------------------------- 217 | 218 | -- Okay, time to parse the input, 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | -- Actually, this code is a big mess, and maybe you should look away. 228 | -- Just scroll on down to `main`, where the exciting stuff happens. 229 | 230 | 231 | 232 | -- No? Okay? I'm sorry ;w; here it is 233 | partial 234 | readNats : String -> List Nat 235 | readNats s = 236 | let (nonDigits, s') = span (not . isDigit) s 237 | (digits, s'') = span isDigit s' 238 | in 239 | case digits of 240 | "" => [] 241 | d => cast d :: readNats s'' 242 | 243 | partial 244 | parseMystery : String -> MysteryInstruction 245 | parseMystery s = 246 | let [m, a, b, c] = readNats s 247 | Just m' = natToFin m 16 248 | Just c' = natToFin c numRegs 249 | in Mystery m' (cast a, cast b, c') 250 | 251 | -- Thank you Anton Trunov (https://stackoverflow.com/a/44083910/257418) 252 | fromListOfLength : (n : Nat) -> (xs : List a) -> Maybe (Vect n a) 253 | fromListOfLength n xs with (decEq (length xs) n) 254 | fromListOfLength n xs | (Yes prf) = rewrite (sym prf) in Just (fromList xs) 255 | fromListOfLength n xs | (No _) = Nothing 256 | 257 | partial 258 | parse : List String -> (List Sample, List MysteryInstruction) 259 | parse (""::""::programLines) = ([], map parseMystery programLines) 260 | parse (b::i::a::""::rest) = 261 | let 262 | Just before = fromListOfLength 4 $ map toIntegerNat (readNats b) 263 | instruction = parseMystery i 264 | Just after = fromListOfLength 4 $ map toIntegerNat (readNats a) 265 | sample = MkSample before instruction after 266 | (ss, ms) = parse rest 267 | in (sample::ss, ms) 268 | 269 | 270 | 271 | 272 | ----------------------------------------------------------------- 273 | 274 | -- Okay, this is where the magic happens!!! 275 | partial 276 | main : IO () 277 | main = do 278 | -- Parse the input. 279 | Right input <- fGetChars stdin 99999 280 | let (samples, program) = parse (lines input) 281 | 282 | putStrLn "Task 1:" 283 | printLn $ length [s | s <- samples, length (candidates s) >= 3] 284 | 285 | putStrLn "Task 2:" 286 | let Just mapping = deduceMapping samples 287 | let Just instructions = sequence (reveal mapping <$> program) 288 | printLn $ foldl (flip execute) [0,0,0,0] instructions 289 | -------------------------------------------------------------------------------- /day-15.rs: -------------------------------------------------------------------------------- 1 | // https://adventofcode.com/2018/day/15 in Rust. 2 | 3 | use std::collections::{HashMap, HashSet}; 4 | use std::io; 5 | use std::io::prelude::*; 6 | use std::iter::Iterator; 7 | 8 | type Integer = i32; 9 | type Coordinate = Integer; 10 | type Distance = Integer; 11 | type HitPoints = Integer; 12 | 13 | /// A point on a two-dimensional grid. 14 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)] 15 | struct Point { 16 | y: Coordinate, 17 | x: Coordinate, 18 | } 19 | 20 | impl Point { 21 | /// Return the Manhattan distance between two points. 22 | fn manhattan_distance(p: Point, q: Point) -> Distance { 23 | (p.x - q.x).abs() + (p.y - q.y).abs() 24 | } 25 | 26 | /// Is this point adjacent to the other one? 27 | fn adjacent_to(&self, other: Point) -> bool { 28 | Point::manhattan_distance(*self, other) == 1 29 | } 30 | 31 | /// Returns the four adjacent points on the grid, in reading order. 32 | fn neighbors(&self) -> Vec { 33 | let Point { x, y } = *self; 34 | return vec![ 35 | Point { x, y: y - 1 }, 36 | Point { x: x - 1, y }, 37 | Point { x: x + 1, y }, 38 | Point { x, y: y + 1 }, 39 | ]; 40 | } 41 | } 42 | 43 | /// A piece of terrain on the battlefield: a wall (`#`) or open space (`.`). 44 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 45 | enum Terrain { 46 | Wall, 47 | Open, 48 | } 49 | 50 | /// The factions warring on the battlefield: goblins (`G`) and elves (`E`). 51 | #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] 52 | enum Faction { 53 | Goblin, 54 | Elf, 55 | } 56 | 57 | /// The terrain map of a battlefield. 58 | type World = HashMap; 59 | 60 | /// A goblin or elf, fighting on the battlefield. 61 | #[derive(Clone, Debug)] 62 | struct Combatant { 63 | faction: Faction, 64 | position: Point, 65 | power: HitPoints, 66 | hit_points: HitPoints, 67 | } 68 | 69 | impl Combatant { 70 | const STANDARD_POWER: HitPoints = 3; 71 | const STANDARD_HIT_POINTS: HitPoints = 200; 72 | 73 | fn make(position: Point, faction: Faction) -> Combatant { 74 | Combatant { 75 | position, 76 | faction, 77 | power: Combatant::STANDARD_POWER, 78 | hit_points: Combatant::STANDARD_HIT_POINTS, 79 | } 80 | } 81 | 82 | fn dead(&self) -> bool { 83 | self.hit_points <= 0 84 | } 85 | 86 | fn alive(&self) -> bool { 87 | !self.dead() 88 | } 89 | } 90 | 91 | /// The outcome of a battle: how many full rounds it lasted, and how many hit 92 | /// points remain in total. (The task asks for the product of these quantities.) 93 | struct Outcome { 94 | round: Integer, 95 | total_hp: HitPoints, 96 | } 97 | 98 | impl Outcome { 99 | fn product(&self) -> Integer { 100 | self.round * self.total_hp 101 | } 102 | } 103 | 104 | /// A battlefield, ready to be simulated. 105 | #[derive(Clone, Debug)] 106 | struct Battlefield { 107 | world: World, 108 | combatants: Vec, 109 | } 110 | 111 | impl Battlefield { 112 | /// Checks if the given points is vacant (free of walls and combatants). 113 | fn is_vacant(&self, p: Point) -> bool { 114 | let occupied = self.combatants.iter().any(|c| c.alive() && c.position == p); 115 | self.world[&p] == Terrain::Open && !occupied 116 | } 117 | 118 | /// Returns a `HashMap` mapping `Point`s reachable from `start` to their 119 | /// distance in steps from `start`. 120 | fn points_reachable_from(&self, start: Point) -> HashMap { 121 | // Our finalized HashMap. 122 | let mut reach: HashMap = HashMap::new(); 123 | 124 | // The running distance, and the set of all points `distance` steps away. 125 | let mut distance: Distance = 0; 126 | let mut frontier: HashSet = HashSet::new(); 127 | frontier.insert(start); 128 | 129 | while !frontier.is_empty() { 130 | // Expand the frontier, inserting all previous points into `reach`. 131 | frontier = frontier 132 | .iter() 133 | .flat_map(|&p| { 134 | reach.insert(p, distance); 135 | p.neighbors() 136 | .iter() 137 | .filter(|&p| self.is_vacant(*p) && !reach.contains_key(p)) 138 | .cloned() 139 | .collect::>() 140 | }) 141 | .collect(); 142 | distance += 1; 143 | } 144 | 145 | reach 146 | } 147 | 148 | /// Simulates a turn for the combatant with the given index into 149 | /// `self.combatants`. They'll try to move, and then try to attack. 150 | fn simulate_turn(&mut self, combatant_index: usize) -> () { 151 | if self.combatants[combatant_index].dead() { 152 | return; 153 | } 154 | 155 | let Combatant { 156 | faction: allied, 157 | position, 158 | hit_points: _, 159 | power, 160 | } = self.combatants[combatant_index]; 161 | let mut here = position; 162 | 163 | // Phase 1: find a target to walk toward. 164 | let mut possible_targets: Vec = self 165 | .combatants 166 | .iter() 167 | .filter(|c| c.alive() && c.faction != allied) 168 | .flat_map(|c| c.position.neighbors()) 169 | .collect(); 170 | 171 | // Don't try to move if we're in a perfect spot to attack already. 172 | if !possible_targets.contains(&here) { 173 | // Okay, let's try to move. Find the closest reachable target: 174 | let reach = self.points_reachable_from(here); 175 | possible_targets.retain(|p| reach.contains_key(p)); 176 | possible_targets.sort(); 177 | let target = possible_targets.iter().min_by_key(|&t| reach[t]); 178 | 179 | match target { 180 | None => { 181 | // No targets found. And if we don't even have anywhere we 182 | // wanna walk, we won't attack either. 183 | return; 184 | } 185 | Some(&t) => { 186 | let distances = self.points_reachable_from(t); 187 | let steps = here.neighbors(); 188 | let choice = steps 189 | .iter() 190 | .filter(|p| distances.contains_key(p)) 191 | .min_by_key(|&p| distances.get(p).unwrap()) 192 | .unwrap(); 193 | 194 | here = *choice; 195 | self.combatants[combatant_index].position = here; 196 | } 197 | } 198 | } 199 | 200 | // Phase 2: try to attack a possible victim. 201 | let victim_index: Option = self 202 | .combatants 203 | .iter() 204 | .enumerate() 205 | .filter(|(_i, c)| c.alive() && c.faction != allied && c.position.adjacent_to(here)) 206 | .map(|(i, c)| (c.hit_points, i)) 207 | .min() 208 | .map(|(_hp, i)| i); 209 | 210 | victim_index.map(|i| self.combatants[i].hit_points -= power); 211 | } 212 | 213 | /// Simulate a round of battle: everyone takes a turn in reading order. 214 | /// After each round, we clean up the bodies. 215 | fn simulate_round(&mut self) -> () { 216 | self.combatants.sort_by_key(|c| c.position); 217 | for i in 0..self.combatants.len() { 218 | self.simulate_turn(i); 219 | } 220 | self.combatants.retain(|c| c.alive()); 221 | } 222 | 223 | /// Simulate the whole battle and return the outcome. 224 | fn simulate_battle(&mut self) -> Outcome { 225 | let mut round = -1; 226 | while self.ongoing() { 227 | round += 1; 228 | self.simulate_round(); 229 | } 230 | let total_hp = self.combatants.iter().map(|c| c.hit_points).sum(); 231 | Outcome { round, total_hp } 232 | } 233 | 234 | /// Simulate the whole battle for elf victory. Once an elf dies, None is 235 | /// returned. If no elves die, Some(outcome) is returned. 236 | fn simulate_elf_victory(&mut self) -> Option { 237 | let mut round = -1; 238 | while self.ongoing() { 239 | round += 1; 240 | let before = self.elf_count(); 241 | self.simulate_round(); 242 | let after = self.elf_count(); 243 | if before > after { 244 | return None; 245 | } 246 | } 247 | let total_hp = self.combatants.iter().map(|c| c.hit_points).sum(); 248 | Some(Outcome { round, total_hp }) 249 | } 250 | 251 | /// Counts elves among the combatants. 252 | fn elf_count(&self) -> usize { 253 | self.combatants 254 | .iter() 255 | .filter(|c| c.faction == Faction::Elf) 256 | .count() 257 | } 258 | 259 | /// Returns whether the battle is ongoing, i.e. both factions are represented. 260 | fn ongoing(&mut self) -> bool { 261 | self.represented(Faction::Elf) && self.represented(Faction::Goblin) 262 | } 263 | 264 | /// Returns whether anyone from the given faction is still alive. 265 | fn represented(&mut self, faction: Faction) -> bool { 266 | self.combatants.iter().any(|c| c.faction == faction) 267 | } 268 | } 269 | 270 | /// Parse a Faction from a character of input. 271 | fn parse_faction(ch: char) -> Faction { 272 | match ch { 273 | 'G' => Faction::Goblin, 274 | 'E' => Faction::Elf, 275 | _ => panic!("Invalid faction."), 276 | } 277 | } 278 | 279 | /// Parse some Terrain from a character of input. 280 | fn parse_terrain(ch: char) -> Terrain { 281 | match ch { 282 | '#' => Terrain::Wall, 283 | '.' | 'G' | 'E' => Terrain::Open, 284 | _ => panic!("Invalid terrain."), 285 | } 286 | } 287 | 288 | /// Parse a Combatant from a character of input. 289 | fn parse_combatant(position: Point, ch: char) -> Option { 290 | match ch { 291 | 'G' | 'E' => Some(Combatant::make(position, parse_faction(ch))), 292 | _ => None, 293 | } 294 | } 295 | 296 | /// Parse a Battlefield from a list of input lines. 297 | fn parse_battlefield(lines: Vec) -> Battlefield { 298 | let mut world = HashMap::new(); 299 | let mut combatants = Vec::new(); 300 | for (y, line) in lines.iter().enumerate() { 301 | for (x, ch) in line.chars().enumerate() { 302 | let point = Point { 303 | x: x as Coordinate, 304 | y: y as Coordinate, 305 | }; 306 | world.insert(point, parse_terrain(ch)); 307 | parse_combatant(point, ch).map(|c| combatants.push(c)); 308 | } 309 | } 310 | Battlefield { world, combatants } 311 | } 312 | 313 | fn main() { 314 | let stdin = io::stdin(); 315 | let lines = stdin.lock().lines().map(|line| line.unwrap()).collect(); 316 | let original = parse_battlefield(lines); 317 | 318 | // Part 1: What's the unmodified outcome? 319 | let mut unmodified = original.clone(); 320 | let outcome = unmodified.simulate_battle(); 321 | println!("Unmodified, the outcome product is {}.", outcome.product()); 322 | 323 | // Part 2: What if we beef up the elves until they stop dying? 324 | for elf_power in Combatant::STANDARD_POWER.. { 325 | // Make all the elves, like, totally kick ass. 326 | let mut what_if = original.clone(); 327 | for c in &mut what_if.combatants { 328 | if c.faction == Faction::Elf { 329 | c.power = elf_power; 330 | } 331 | } 332 | 333 | // See if they win without dying now. 334 | match what_if.simulate_elf_victory() { 335 | None => {} 336 | Some(outcome) => { 337 | println!( 338 | "At power {}, for the first time, no elves die, and the outcome product is {}.", 339 | elf_power, 340 | outcome.product() 341 | ); 342 | break; 343 | } 344 | } 345 | } 346 | } 347 | --------------------------------------------------------------------------------