# Advent of Code 2021: Day 3

Andrew Fontaine <andrew@afontaine.ca>Okay, the submarine is moving, we can see where we’re going. Now to make sure systems are ✔.

Today’s struggle in learning was motivation. Fridays are tricky that way, sometimes it’s a mad rush to get everything done in time for Saturday, others it’s just a slog to the weekend. Today was the latter.

I found the first problem’s trickiest bit to be how to parse the list properly. Only after eating dinner and not being distracted by hunger did the solution become obvious.

## Parsing blues

First, I make a bit type:

```
(* bits are one or zero *)
type bit = One | Zero
(* parse a character into a bit *)
let bit_of_char c =
match c with '1' -> One | '0' -> Zero | c -> raise (BadBit c)
(* turn a bit into a string for combining later *)
let string_of_bit b =
match b with
| One -> "1"
| Zero -> "0"
(* take a list of bits and turn that into a number *)
let int_of_bit r =
List.fold_left (fun b x -> b ^ (string_of_bit x)) "0b" r
|> int_of_string
```

Next, I map the list of codes from the rows to the columns:

```
let input = Core.In_channel.read_lines "./priv/2021/D03"
|> List.map (fun row -> List.map bit_of_char (Core.String.to_list row))
let p1_input =
match input with
| [] -> raise (BadBit 'l')
| hd :: t -> List.fold_left (fun result row ->
List.map2 (fun r bit -> bit :: r) result row) (List.map (fun x -> [x]) hd) t
```

Ocaml has the lovely `map2`

function to map 2 lists together. I inject
the first row, split into lists of single bits, to start the rows off.
Then, I fold over the rest of the rows, adding each character to the
list in the correct column. The end result:

```
[1, 2, 3] [1, 4, 7]
[4, 5, 6] becomes [2, 5, 8]
[7, 8, 9] [3, 6, 9]
```

The rest of the problem is simple. Mark the most common bit in each column, and multiply the resulting final numbers together:

```
let counts row =
List.fold_left
(fun (one, zero) b ->
match b with One -> (one + 1, zero) | Zero -> (one, zero + 1))
(0, 0) row
let greatest_bit (one, zero) = if one >= zero then One else Zero
let p1_sol input =
let g, e =
List.fold_left
(fun (g, e) row ->
let c = counts row in
match greatest_bit c with
| One -> (g ^ "1", e ^ "0")
| Zero -> (g ^ "0", e ^ "1"))
("0b", "0b") input
in
(int_of_string g, int_of_string e)
let p1 =
let g, e = p1_sol p1_input in
let sol = g * e in
Int.to_string sol
```

⭐ one done!

## Problem 2

Problem 2, while similar, certainly starts to raise the difficultly. I fear for the weekend challenges now!

In essence, I am seeking for a specific row. That row needs to match a set of rules, where, for one, we chase the most common bit, and the other, the least common. It took me a few re-reads before I understood the problem at hand. This one is fun, in that there’s a good amount of re-usable code between both seeking:

```
let consider f input =
let firsts = List.map (fun r -> match r with
| ([], _) -> raise (BadBit 'l')
| (h :: _, _) -> h) input in
let bit = greatest_bit (counts firsts) in
List.filter (fun r -> match r with
| ([], _) -> raise (BadBit 'l')
| (h :: _, _) -> f bit h) input
|> List.map (fun (r, i) -> match r with
| [] -> raise (BadBit 'c')
| _ :: t -> (t, i))
let oxygen bit h = bit = h
let co2 bit h = bit <> h
let rec find crit input =
match input with
| [] -> -1
| [(_, x)] -> x
| list -> let results = consider crit list in find crit results
let compute input crit =
let i = List.mapi (fun x r -> (r, x)) input in
find crit i
|> List.nth input
|> int_of_bit
let p2_sol input =
let co2_result = compute input co2 in
let oxygen_result = compute input oxygen in
string_of_int (co2_result * oxygen_result)
let p2 = p2_sol input
```

`find`

is the main entry point into the interesting stuff. It
recursively seeks the index of the row that matches the specific
criteria. `consider`

takes the criteria, puts together the first column
of numbers, and determines which rows make the cut. It also lops off the
considered column, so the recursive `find`

call eventually goes through
all the columns. The criteria are encoded as `oxygen`

(the most common
bit) and `co2`

(the least common bit). Once the right index is found,
its full row is retrieved and parsed into a number to find the final
solution.

This one I found the parsing to be the most painful part. Once I was
over that hurdle, it was smooth sailing. Eventually I hit a few bumps.
There’s an extra rule in part 2 where `One`

wins tie breakers for the
most common bit that I missed, and it took some puzzling to realize I
*wasn’t* lopping off the column in the beginning. Once those two issues
were caught, the ⭐ was mine.

Now if you’ll excuse me, Day 4 just unlocked, and I better see how much of a crunch my weekend will be.

#### Want to discuss this post?

Reach out via email to ~afontaine/blog-discuss@lists.sr.ht, and be sure to follow the mailing list etiquette.

Other posts