{"id":16870419,"url":"https://github.com/narimiran/adventofcode2019","last_synced_at":"2026-02-10T09:07:23.929Z","repository":{"id":115374677,"uuid":"224712720","full_name":"narimiran/AdventOfCode2019","owner":"narimiran","description":"My OCaml solutions for Advent of Code 2019","archived":false,"fork":false,"pushed_at":"2024-11-29T18:07:03.000Z","size":82,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-08-24T16:59:06.915Z","etag":null,"topics":["advent","advent-of-code","advent-of-code-2019","adventofcode","adventofcode2019","functional","functional-programming","ocaml"],"latest_commit_sha":null,"homepage":null,"language":"OCaml","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/narimiran.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2019-11-28T18:54:56.000Z","updated_at":"2024-11-29T18:07:07.000Z","dependencies_parsed_at":"2025-05-07T02:56:15.371Z","dependency_job_id":null,"html_url":"https://github.com/narimiran/AdventOfCode2019","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/narimiran/AdventOfCode2019","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/narimiran%2FAdventOfCode2019","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/narimiran%2FAdventOfCode2019/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/narimiran%2FAdventOfCode2019/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/narimiran%2FAdventOfCode2019/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/narimiran","download_url":"https://codeload.github.com/narimiran/AdventOfCode2019/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/narimiran%2FAdventOfCode2019/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272267826,"owners_count":24903785,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-26T02:00:07.904Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["advent","advent-of-code","advent-of-code-2019","adventofcode","adventofcode2019","functional","functional-programming","ocaml"],"created_at":"2024-10-13T15:04:17.196Z","updated_at":"2026-02-10T09:07:22.333Z","avatar_url":"https://github.com/narimiran.png","language":"OCaml","readme":"# Advent of Code 2019\n\nAll my Advent of Code repos:\n\n* [AoC 2015 in Nim, Python](https://github.com/narimiran/advent_of_code_2015)\n* [AoC 2016 in Python, Clojure (+ visualizations)](https://github.com/narimiran/advent_of_code_2016)\n* [AoC 2017 in Nim, OCaml, Python](https://github.com/narimiran/AdventOfCode2017)\n* [AoC 2018 in Nim, Python, Racket](https://github.com/narimiran/AdventOfCode2018)\n* [AoC 2019 in OCaml, Python](https://github.com/narimiran/AdventOfCode2019) (this repo)\n* [AoC 2020 in Nim, one liner-y Python, Racket](https://github.com/narimiran/AdventOfCode2020)\n* [AoC 2021 in Python, Racket](https://github.com/narimiran/AdventOfCode2021)\n* [AoC 2022 in Python, Clojure](https://github.com/narimiran/AdventOfCode2022)\n* [AoC 2023 in Clojure](https://github.com/narimiran/AdventOfCode2023)\n* [AoC 2024 in Clojure (Clerk notebooks)](https://github.com/narimiran/aoc2024)\n\n\n\u0026nbsp;\n\n\n## Solutions, highlights and thoughts\n\nMy first time solving AoC puzzles in OCaml from the get-go.\n(I've used OCaml for [AoC 2017](https://github.com/narimiran/AdventOfCode2017),\nbut only after I've solved all tasks with other languages first -- as a preparation for this year.)\n\nTo keep this readme at a reasonable vertical size when rendered,\nhighlights and thoughts for each day are hidden behind the \"details\" flag.\nClick on it if you want to read my ramblings.\n\n([Python solutions](python/) were added at a much later date.)\n\n\n\n### Day 1\n\n[The Tyranny of the Rocket Equation](http://adventofcode.com/2019/day/1) || [day01.ml](ocaml/day01.ml) || Runtime: 0.6 ms\n\n\u003cdetails\u003e\n\nWe have two slightly different functions (`f`) for each part, so counting the total boils down to:\n```ocaml\nList.fold_left (fun acc x -\u003e acc + f x) 0\n```\n\n\u003c/details\u003e\n\n\n\n### Day 2\n\n[1202 Program Alarm](http://adventofcode.com/2019/day/2) || [day02.ml](ocaml/day02.ml) || Runtime: 0.9 ms\n\n\u003cdetails\u003e\n\nOur inputs are such that there is no need to iterate through all possible `verb`s,\nwe can always leave `verb` at zero and later on calculate it from the difference between the desired and given output.\n```ocaml\nlet result = intcode |\u003e set_up [(1, noun)] |\u003e run in\nlet verb = output - result in\nif verb \u003c 100 then\n  100 * noun + verb\n```\n\n\u003c/details\u003e\n\n\n\n### Day 3\n\n[Crossed Wires](http://adventofcode.com/2019/day/3) || [day03.ml](ocaml/day03.ml) || Runtime: 50 ms\n\n\u003cdetails\u003e\n\nInitial idea was to create a `Map` for every wire and then to find intersections via `Map.merge`:\n```ocaml\nlet path_a = follow wire_a\nlet path_b = follow wire_b\n\nlet intersections = find_intersections path_a path_b\n\nlet () =\n  intersections |\u003e find closest;\n  intersections |\u003e find shortest\n```\nAlthough elegant, it was very inefficient. (Runtime around 180 ms)\n\nCurrent solution treats wires differently:\nThe first wire is used to populate a `Hashtbl` with all visited points as keys and the number of steps taken as values.\nThe second wire is used only to check for intersections.\nThis gives 3.5x performance boost.\n```ocaml\nlet () =\n  wire_a |\u003e visit_all_points;\n  wire_b |\u003e find_intersections;\n\n  intersections |\u003e find closest;\n  intersections |\u003e find shortest\n```\n\n\u003c/details\u003e\n\n\n\n### Day 4\n\n[Secure Container](http://adventofcode.com/2019/day/4) || [day04.ml](ocaml/day04.ml) || Runtime: 0.6 ms\n\n\u003cdetails\u003e\n\nThe initial solution iterated through the whole range between `low` and `high`,\nconverting each number to `String` or `OSeq` (I've tried both versions):\n```ocaml\nlet solve ~part =\n  let f = if part = 1 then ( \u003e= ) else ( = ) in\n  let res = ref 0 in\n  for i = low to high do\n    let sq = String.to_seq (string_of_int i) in\n    if not_decr sq then\n      let groups = sq |\u003e OSeq.group ~eq:Char.equal in\n      if OSeq.exists (fun g -\u003e f (OSeq.length g) 2) groups then incr res\n  done;\n  !res\n```\nThis had 1.3 **b**illion instructions and its runtime was around 130 ms.\n\nCurrent solution is much uglier: it has six for-loops to iterate on each digit,\nwhere lower bound for each digit is dependant on the digit before it.\nAlso, we iterate over potential candidates only once, and test for both parts:\n```ocaml\nlet digit_groups = [ a; b; c; d; e; f ] |\u003e group_lengths in\nif digit_groups |\u003e has_multiples then incr part_1;\nif digit_groups |\u003e has_duplicates then incr part_2\n```\nwhere `group_lengths` is a specialised and optimized version of `CCList.group_succ`\njust for this task:\nNo unnecessary creations of lists of groups and `List.rev`,\nwe're interested only in number of members of each group of the same digit:\n```ocaml\nlet group_lengths l =\n  let rec aux acc cur amnt l =\n    match cur, l with\n    | _, [] -\u003e amnt :: acc\n    | y, x :: tl when Int.equal x y -\u003e aux acc x (amnt+1) tl\n    | _, x :: tl -\u003e aux (amnt :: acc) x 1 tl\n  in\n  aux [] 0 0 l\n```\n\nThe result of changing the algorithm and applying these micro-optimisations?\n900k instructions (1500 times less than original!) and its runtime is 0.6 ms.\nIf it is ugly but it works... :)\n\nBonus: the main function looks like `\u003e\u003e=`.\n\n\u003c/details\u003e\n\n\n\n### Day 5\n\n[Sunny with a Chance of Asteroids](http://adventofcode.com/2019/day/5) || [day05.ml](ocaml/day05.ml) || Runtime: 0.7 ms\n\n\u003cdetails\u003e\n\nBelow are the notes from the original version of `day05.ml`, together with the bug that\nhad bit me later on Day 9 (`let dest = a.(ip+3)`).\nAfter Day 9 was released, the common logic from days 2, 5, 7 and 9 was extracted to\n`intcode` module, and the solutions for those days were vastly simplified.\n(See [\"Intcode int'mezzo\"](#intcode-intmezzo) below for more details.)\n\n----\n\nAfter you finally manage to read and understand what the instructions want from you,\nthe task becomes quite straight-forward.\nNot counting the warm-up task on Day 1, I would say this was the easiest one so far:\nTake Day 2, add new opcodes, change some details, and you're done.\n\nOur \"intcode computer\" is starting to evolve and soon enough (but not yet)\nthese things should be probably put in a separate module which will be used by multiple tasks.\n\nThe initial solution had 8 separate branches for 8 separate opcodes.\nSimple and straightforward, but lots of unnecessary duplication:\nI've noticed that I can group together opcodes 1\u00262, 5\u00266, and 7\u00268 —\nthe only difference between them was the function/operation involved,\nso the logical thing to do was to define custom operator for each group:\n```ocaml\nlet ( +|* ) = if op = 1 then ( + ) else ( * ) in\na.(dest) \u003c- n +|* v\n\nlet ( \u003c\u003e|= ) = if op = 5 then ( \u003c\u003e ) else ( = ) in\nif n \u003c\u003e|= 0 then v else ip+3\n\nlet ( \u003c|= ) = if op = 7 then ( \u003c ) else ( = ) in\na.(dest) \u003c- if n \u003c|= v then 1 else 0\n```\n\nOperation deduplication half way done.\nGroups 1\u00262 and 7\u00268 still had a lot of things in common\n(they both read two parameters, have the same destination location (`ip+3`), do the same jump (`ip+4`))\nso in the end they were put in the same branch to cut duplicated stuff some more:\n```ocaml\nlet noun = read_param 1 in\nmatch a.(ip) mod 100 with\n| 1 | 2 | 7 | 8 as op -\u003e\n  let verb = read_param 2 in\n  let dest = a.(ip+3) in\n  a.(dest) \u003c-\n    (match op with\n     | 1 -\u003e noun + verb\n     | 2 -\u003e noun * verb\n     | 7 -\u003e CCBool.to_int (noun \u003c verb)\n     | 8 -\u003e CCBool.to_int (noun = verb)\n     | _ -\u003e failwith \"ocaml, you silly\");\n  ip+4\n```\n\nYes I've removed some of the custom operators defined above, so some duplication is reintroduced.\nI find it more readable this way.\n\n\u003c/details\u003e\n\n\n\n### Day 6\n\n[Universal Orbit Map](http://adventofcode.com/2019/day/6) || [day06.ml](ocaml/day06.ml) || Runtime: 3 ms\n\n\u003cdetails\u003e\n\nUngh! Quest for my future self:\ncan you understand all the functions your previous self has written here?\n\nWe go through the input and create two `Map`s, one (used for the first part of the task)\ncontaining `parent -\u003e children` relationships (called `p2c`),\nand the other containing `kid -\u003e parent` relationships (called `k2p`) for the second part.\n\nPart 1 is the recursive traversal through `\"COM\"`'s children, their children,\ntheir children's children, ... counting the total distance to `\"COM\"`:\n```ocaml\nlet rec traverse n key =\n  match children with\n  | [] -\u003e n\n  | _ -\u003e\n    let children_distances = List.map (traverse (n+1)) children in\n    n + List.fold_left (+) 0 children_distances\n```\n\nThe second part first builds the list of all ancestors for `\"YOU\"` and `\"SAN\"`:\n```ocaml\nlet rec traverse relations acc = function\n  | \"COM\" -\u003e acc\n  | kid -\u003e\n    let parent = relations |\u003e RelationMap.find kid in\n    traverse relations (parent::acc) parent\n```\n\nBoth of those lists start with `\"COM\"` and all the common ancestors for both `\"YOU\"` and `\"SAN\"`.\nWe need to remove those, and what remains is the answer for the second part:\n```ocaml\nlet rec calc_orbital_transfers you san =\n  match you, san with\n  | x::xs, y::ys when x = y -\u003e calc_orbital_transfers xs ys\n  | _, _ -\u003e List.length you + List.length san\n```\n\n\u003c/details\u003e\n\n\n\n### Intcode int'mezzo\n\n[intcode.ml](ocaml/lib/intcode.ml)\n\n\u003cdetails\u003e\n\nFrom Day 5 notes:\n\n\u003e Our \"intcode computer\" is starting to evolve and soon enough (but not yet)\n\u003e these things should be probably put in a separate module which will be used\n\u003e by multiple tasks.\n\nThe refactoring time has come.\n\nWhen Day 7 was released, I wasn't at home so I couldn't solve it at that time.\nI've read the task and realised that my current implementation from Day 5 won't fit for it,\nI would need to refactor it so the state between runs remains preserved.\n\nIn the mean time, Day 9 was released, and with it our \"intcode computer\" implementation\nis complete.\nA perfect time to extract all the useful functions in a separate module.\n\nOur computer can be in three states:\n1. running - executing instructions until one of two things happen:\n2. waiting - computer's input queue is empty and it can't continue until it receives an input\n3. halted - computer has reached intcode 99\n\nThe computer is now represented as a `record`:\n```ocaml\ntype state = Running | Waiting | Halted\n\ntype computer = {\n  ram : int array;\n  ip : int;\n  rp : int;\n  state : state;\n  in_queue : int Queue.t;\n  out_queue : int Queue.t;\n}\n```\nwhere `ip` and `rp` are instruction and relative pointers, respectively, and\n`in_queue` and `out_queue` are FIFO queues.\n\nComputer initialization supports specifying arbitrary RAM size (with the 4096 as the default).\n```ocaml\nlet initialize_computer ?(ram_size=4096) instructions =\n  let ram = initialize_memory ram_size instructions in\n  let in_queue = Queue.create () in\n  let out_queue = Queue.create () in\n  { ram; ip = 0; rp = 0; state = Running; in_queue; out_queue }\n```\n\nWe can write to `in_queue` and read from `out_queue`, either the next output value\n(days 7, 9, 11) or the last value in the queue (Day 5).\n```ocaml\nlet receive value comp =\n  Queue.add value comp.in_queue;\n  comp\n\nlet get_next_output comp =\n  Queue.take comp.out_queue\n\nlet get_last_output comp =\n  comp.out_queue\n  |\u003e Queue.to_seq\n  |\u003e OSeq.reduce (fun _ v -\u003e v)\n```\n\nWhen the computer has stopped (either waiting or halted), the whole state is\nreturned as different tasks need different values from it.\n```ocaml\nlet run_until_halt comp =\n  let rec run comp =\n    match comp.state with\n    | Halted | Waiting -\u003e comp\n    | Running -\u003e comp |\u003e execute_opcode |\u003e run\n  in\n  { comp with state = Running } |\u003e run\n```\n\n\u003c/details\u003e\n\n\n\n### Day 7\n\n[Amplification Circuit](http://adventofcode.com/2019/day/7) || [day07.ml](ocaml/day07.ml) || Runtime: 50 ms\n\n\u003cdetails\u003e\n\nNow that [my intcode module](ocaml/lib/intcode.ml) is complete, the main problem\nof this task becomes how to repeatedly loop through the amplifiers until one\nof them halts.\n\n[`CCList.fold_map`](https://c-cube.github.io/ocaml-containers/dev/containers/CCList/index.html#val-fold_map)\nproved to be very useful for this:\nIt takes an accumulator (just like the regular `fold_left`), which in our case is\nthe output of the previous computer/amplifier; and it returns a tuple containing\nboth the accumulator and the modified list (like `map`), which are our computers/amplifiers\nafter they had run this time.\nWe need both of those outputs.\n\nUsing that function, we can recursively run all of our computers until one of them halted:\n```ocaml\nlet rec get_output (score, computers) =\n  if some_halted computers then score\n  else\n    computers\n    |\u003e CCList.fold_map\n      (fun last_output comp -\u003e\n         let comp' =\n           comp\n           |\u003e Intcode.receive last_output\n           |\u003e Intcode.run_until_halt in\n         comp'.output, comp')\n      score\n    |\u003e get_output\n```\n\nWith that in place, finding the solution for both parts is just a matter of running\nall the permutations of phase settings, and finding the maximal output:\n```ocaml\nlet solve =\n  permutations\n  %\u003e List.fold_left\n    (fun acc perm -\u003e\n       let computers = create_computers perm in\n       (0, computers) |\u003e get_output |\u003e max acc)\n    0\n```\n\n\u003c/details\u003e\n\n\n\n### Day 8\n\n[Space Image Format](http://adventofcode.com/2019/day/8) || [day08.ml](ocaml/day08.ml) || Runtime: 2 ms\n\n\u003cdetails\u003e\n\nThis is an easy one after Day 7, which was the hardest one so far for me.\n\nThe first part is boring:\nWe count the number of each digit per layer, and find the layer with the fewest zeros.\n\nThe second part is more interesting.\nFor each pixel, we recursively try to find its color, starting from the top-most\nlayer and until we reach a layer with a non-transparent pixel.\nNot a very efficient way of doing things (we repeatedly go through the list of layers,\nand then for each layer we go to `nth` pixel), but it wins for its simplicity:\n```ocaml\nlet rec pixel_color layers pixel =\n  match layers with\n  | [] -\u003e failwith \"pixel is transparent\"\n  | layer :: below -\u003e\n    (match List.nth layer pixel with\n     | '0' -\u003e ' '\n     | '1' -\u003e '#'\n     | '2' -\u003e pixel_color below pixel\n     | _ -\u003e failwith \"invalid input\")\n```\n\n\u003c/details\u003e\n\n\n\n### Day 9\n\n[Sensor Boost](http://adventofcode.com/2019/day/9) || [day09.ml](ocaml/day09.ml) || Runtime: 21 ms\n\n\u003cdetails\u003e\n\nThis task has brought a relative pointer (`rp`) and a new mode (`2`):\n```ocaml\n  match mode with\n  | 0 -\u003e param_val\n  | 1 -\u003e ip + param\n  | 2 -\u003e rp + param_val\n```\n\nWith that in place, the [intcode computer](ocaml/lib/intcode.ml) is now complete.\n\nThis felt even easier than Day 5.\n\n\u003c/details\u003e\n\n\n\n### Day 10\n\n[Monitoring Station](http://adventofcode.com/2019/day/10) || [day10.ml](ocaml/day10.ml) || Runtime: 33 ms\n\n\u003cdetails\u003e\n\nAt first I didn't even bother to solve this one because all I could think of\nwas some lousy `O(n^2)` algorithm, and \"Surely, that can't be it, I need\nsomething more clever than that!\".\nAs it turns out, there's no need to be more clever than that\n(and don't call me Shirley!).\n\nInitially I solved the first part without `atan2` because I was afraid of\nfloating point errors:\n```ocaml\nlet slope (x1, y1) (x2, y2) =\n  let dx = x2 - x1 in\n  let dy = y2 - y1 in\n  let d = gcd dx dy |\u003e abs in\n  let dx' = dx / d in\n  let dy' = dy / d in\n  (dx', dy')\n```\n\nLater on, for the second part, I decided to use `atan2` to keep things simple,\nso I refactored everything to use it.\n\nThe first thing to notice in the task is that we are *not* in the usual\nright-hand Cartesian coordinate system (where y-axis is counter-clockwise\nfrom the x-axis), but in the left-hand one (y-axis points downwards).\nOr to put it differently, instead of the usual `x-y` coordinate system,\nwe are in the rotated-clockwise `y-x` (right-hand!) coordinate system.\n\nThis means we can still have the meaningful results of `atan2` by providing\nits arguments in reverse (`atan2 x y` instead of the usual `atan2 y x`).\n\nTo find the 200th asteroid in the clockwise direction starting from pointing\nupwards, we need to sort the angles from the largest to smallest:\n```ocaml\nlet angle_cmp (phi1, _) (phi2, _) = - Float.compare phi1 phi2\n```\n\nFortunately, `Float.compare` correctly deals with any floating point inaccuracies,\nso we're able to filter out all asteroids with the same relative angle to\nour monitoring station:\n```ocaml\nasterioids\n|\u003e relative_locations station\n|\u003e OSeq.sort_uniq ~cmp:angle_cmp\n|\u003e OSeq.nth 199\n```\n\n\u003c/details\u003e\n\n\n\n### Day 11\n\n[Space Police](http://adventofcode.com/2019/day/11) || [day11.ml](ocaml/day11.ml) || Runtime: 13 ms\n\n\u003cdetails\u003e\n\nWe are once again in left-hand Cartesian coordinate system with y-axis pointing downwards,\nand we must take that into an account when making turns.\n\nA direction is defined as `(x, y)` tuple with possible values `(1, 0)` (right),\n`(-1, 0)` (left), `(0, 1)` (down!), and `(0, -1)` (up!).\nTo make a turn we use the following function:\n```ocaml\nlet rotate (x, y) = function\n  | Left -\u003e (y, -x)\n  | Right -\u003e (-y, x)\n```\n\nPainting the hull is a matter of recursively following the rules of the task,\nuntil the computer halts:\n\n1. read input from the current position\n1. run computer until it can't run no more (`Waiting` state)\n1. read the two outputs (`color` and `turn`, respectively)\n1. paint the current position\n1. change direction\n1. move to the next position\n\n```ocaml\nlet input = panels |\u003e PanelMap.get_or ~default:0 pos in\nlet comp' =\n  comp\n  |\u003e Intcode.receive input\n  |\u003e Intcode.run_until_halt in\nlet color = comp' |\u003e Intcode.get_next_output in\nlet turn = comp' |\u003e Intcode.get_next_output |\u003e Turn.of_int in\nlet panels' = panels |\u003e PanelMap.add pos color in\nlet dir' = turn |\u003e Turn.rotate dir in\nlet pos' = Coord.(pos + dir') in\npaint panels' pos' dir' comp'\n```\n\n\u003c/details\u003e\n\n\n\n### Day 12\n\n[The N-Body Problem](http://adventofcode.com/2019/day/12) || [day12.ml](ocaml/day12.ml) || Runtime: 40 ms\n\n\u003cdetails\u003e\n\nThe famous n-body problem! Woohoo!\n\nWhen I saw the input, I immediately remembered a\n[similar task (\"Particle Swarm\") from AoC 2017](https://adventofcode.com/2017/day/20).\nNot because I have a really good memory (I don't), but because I've solved AoC 2017 just\none month ago, as a preparation for this year.\n\nSo let's reuse some data-types from\n[that solution](https://github.com/narimiran/AdventOfCode2017/blob/master/ocaml/day20.ml),\nit might be an overkill but who knows, it may be useful for the second part:\n```ocaml\ntype coord = { x : int; y : int; z : int }\ntype moon = { p : coord; v : coord }\n```\n\nThere is also no need for regex, `sscanf` does the job very well for these kinds\nof inputs:\n```ocaml\nlet zeros = { x = 0; y = 0; z = 0 }\n\nlet create_moon x y z = {\n  p = { x; y; z };\n  v = zeros;\n}\n\nlet parse_line line =\n  Scanf.sscanf\n    line\n    \"\u003cx=%d, y=%d, z=%d\u003e\"\n    create_moon\n```\n\nFollowing the instructions on how to first calculate and then apply the gravity\nto velocity, followed by applying velocity to positions, the first part is just\nrunning the simulation with 1000 time steps, and calculating the total energy\nof the system:\n```ocaml\nlet time_step moons =\n  moons\n  |\u003e List.map (apply_gravity moons)\n  |\u003e List.map apply_velocity\n\nlet part_1 =\n  let open CCFun in\n  simulate 1000\n  %\u003e List.map total_energy\n  %\u003e List.fold_left (+) 0\n```\n\nAnd then... the second part appears.\nThe first thing to do is just to ignore the instructions, which say\n*\"the universe might last for a very long time before repeating\"*.\n\nAfter a while, a change of mind.\nOk, we might want to listen to the instructions after all :)\n\nA thought comes to my mind:\nThere are multiple bodies which behave periodically, but each (probably) has\nits own period.\nWe need to break this problem down into smaller ones, calculate the periods\nfor each smaller problem and then find the least common multiple.\n\nThe question remains: what are the smaller problems here?\nI had several wrong guesses before it dawned on me: the directions\nare independent of each other.\n\nThat discovery is the best part of the solution for part 2.\nThe code is quite ugly and I won't be posting it here.\n\nThere are several parts where you can potentially make your code faster.\n- The first thing to notice is that you don't have to save every configuration,\n  the system will return at its original configuration.\n- The second thing that speeds things up is that you can only look for the time\n  where the velocities for each moon will return to its original values (zeros).\n  This happens twice per period (the initial state, and then the \"farthest\" state),\n  so we can find when this happens and then multiply the result by 2.\n\n\u003c/details\u003e\n\n\n\n### Day 13\n\n[Care Package](http://adventofcode.com/2019/day/13) || [day13.ml](ocaml/day13.ml) || Runtime: 38 ms\n\n\u003cdetails\u003e\n\nAnother odd day, another intcode task.\n\nThe first part is straight-forward.\nRun the computer until it is halted, and then go through the outputs to count\nhow many times you encounter a `Block` tile.\nThis could have been done simply by counting number of `2`s seen, but that\nfelt like a magic number, so I created a `Tile` module, so that there is no\nconfusion about it:\n```ocaml\nmodule Tile = struct\n  type t = Empty | Wall | Block | Paddle | Ball\n\n  let of_int = function\n    | 0 -\u003e Empty\n    | 1 -\u003e Wall\n    | 2 -\u003e Block\n    | 3 -\u003e Paddle\n    | 4 -\u003e Ball\n    | _ -\u003e failwith \"invalid tile\"\nend\n```\n\nThe second part was very problematic for me because I didn't understand that we\nneed to read *all* the outputs every time we halt.\nI first thought it was just three outputs each time, and it took me a lot of\ntime to figure that one out.\nI would have appreciated a more detailed instructions for the second part.\n\nHaving a `Tile` module also makes finding a paddle and a ball more readable\nand understandable than just having some \"magic numbers\" like 3 and 4:\n\n```ocaml\nlet (x, y, t) = comp |\u003e Intcode.get_next_3_outputs in\nif (x, y) = (-1, 0) then score := t\nelse\n  match Tile.of_int t with\n  | Tile.Paddle -\u003e paddle_pos := x\n  | Tile.Ball -\u003e ball_pos := x\n  | _ -\u003e ()\n```\n\n\u003c/details\u003e\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnarimiran%2Fadventofcode2019","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnarimiran%2Fadventofcode2019","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnarimiran%2Fadventofcode2019/lists"}