Architeuthis
It’s not always easy to distinguish between existentialism and a bad mood.
I almost got done in by floating point arithmetic, I think
8-2 commentary
Used the coordinates of every two same type frequences to create the ilnear equation (y = ax + b) and then fed it all the matrix coordinates to see which belonged to the line. To get the correct number of antinodes I had to check for |y - ax - b| < 0.0001, otherwise I got around 20 too few.
My graph search solves 7-1 and passes the example cases for 7-2, but gives too low a result for the complete puzzle input, and there’s no way I’m manually going through every case to find the false negative. On to day 8 I guess.
7-2 Check case by simple graph search that mostly works
// F#
let isLegit ((total: int64), (calibration : int64 array)) =
let rec search (index : int) (acc: int64) =
let currentValue = calibration.[index]
[Add; Times; Concat] // operators - remove 'Concat' to solve for 7-1
|> List.exists (fun op -> // List.exists returns true the first time the lambda returns true, so search stops at first true
match op with // update accumulator
| Add -> acc + currentValue
| Times -> acc * currentValue
| Concat -> int64 (sprintf "%d%d" acc currentValue)
|> function // stop search on current accumulator value (state) exceeding total, or being just right
| state when state > total -> false
| state when state = total && index < (calibration.Length-1) -> false // this was the problem
| state when state = total && index = (calibration.Length-1) -> true
| state -> // stop if index exceeds input length, or continue search
if index+1 = calibration.Length
then false
else search (index+1) state
)
// start search from second element using the first as current sum
search 1 calibration.[0]
EDIT: total && index < (calibration.Length-1) -> false – i.e. stop if you reach the total before using all numbers, well, also stops you from checking the next operator, So, removing it worked.
Rubber ducking innocent people on the internets works, who knew.
tl;dr: Day 5 was most perfectly fine code thrown out for me, because I ran face first into eliminating imaginary edge cases instead of starting out simple.
5-1 commentary
I went straight into a rabbit hole of doing graph traversal to find all implicit rules (i.e. 1|2, 2|3, 3|4 imply 1|3, 1|4, 2|4) so I could validate updates by making sure all consequent pairs appear in the expanded ruleset. Basically I would depth first search a tree with page numbers for nodes and rules for edges, to get all branches and recombine them to get the full ruleset.
So ideally 1|2, 2|3, 3|4 -> 1|2|3|4 -> 1|2, 2|3, 3|4, 1|3, 1|4, 2|4
Except I forgot the last part and just returned the branch elements pairwise in sequence, which is just the original rules, which I noticed accidentally after the fact since I was getting correct results, because apparently it was completely unnecessary and I was just overthinking myself into several corners at the same time.
5-2 commentary and some code
The obvious cornerstone was the comparison function to reorder the invalid updates, this is what I came up with:
let comparerFactory (ruleset: (int*int) list) :int -> int -> int =
let leftIndex =
ruleset
|> List.groupBy fst
|> List.map (fun (key,grp)-> key, grp |> List.map snd)
|> Map.ofList
fun page1 page2 ->
match (leftIndex |> Map.tryFind page1) with
| Some afterSet when afterSet |> List.contains page2 -> -1
| _ -> 1
The memoization pattern is for caching an index of rules grouped by the before page, so I can sort according to where each part of the comparison appears. I started out with having a second index where the key was the ‘after’ page of the rule which I would check if the page didn’t appear on the left side of any rule, but it turned out I could just return the opposite case, so again unnecessary.
discussion
Same, except in 4-1 I used a recursive function to traverse each direction according to the offset decided by the selected direction (like SW is row++,col–) , due to functional programming induced brain damage.
Would have been pretty useful too if 4-2 turned out to be about finding longer patterns, instead of smaller and in half the directions.
Got stuck forever on 2-2 because of an edge case that only showed up in 7/1000 reports, ended up just brute forcing it, just ran the fitness function after removing one element at a time sequentially.
Then solved 3.x in like minutes because I could be worse at regex, posting code mostly because no-one else posted F# yet.
edited to fix spoiler header formatting
3-2 in F#
"./input.actual"
|> System.IO.File.ReadAllText
|> fun source ->
System.Text.RegularExpressions.Regex.Matches(source, @"don't\(\)|do\(\)|mul\((\d+),(\d+)\)")
|> Seq.fold
(fun (acc, enabled) m ->
match m.Value with
| "don't()" -> acc, false
| "do()" -> acc, true
| mul when enabled && mul.StartsWith("mul") ->
let (x, y) = int m.Groups.[1].Value, int m.Groups.[2].Value
acc + x * y, enabled
| _ -> acc, enabled )
(0, true)
|> fst
|> printfn "The sum of all valid multiplications with respect to do() and don't() is %A"
comments
Not much to say, the regex grabs all relevant strings and the folding function propagates a flag that flips according to do/don’t and an accumulator that is increased when a mul() is encountered and parsed.
The old place on reddit has a tweet up by aella where she goes on a small evo-psych tirade about how since there’s been an enormous amount of raid related kidnapping and rape in prehistory it stands to reason that women who enjoyed that sort of thing had an evolutionary advantage and so that’s why most women today… eugh.
I wonder where the superforecasters stand on aella being outed as a ghislain maxwell type fixer for the tescreal high priesthood.