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.
4-1
Inlined some stuff to fit everything in one function:
let rec isXmas (row:int) (col:int) (dir:int) (depth:int) (arr: char array2d) : bool =
let value = arr.[row,col]
if depth = 3
then value = 'S'
else if [|'X';'M';'A';'S'|].[depth] = value
then
let (nextRow, nextCol) =
match dir with
| 1 -> row + 1, col - 1
| 2 -> row + 1, col
| 3 -> row + 1, col + 1
| 4 -> row, col - 1
| 6 -> row, col + 1
| 7 -> row - 1, col - 1
| 8 -> row - 1, col
| 9 -> row - 1, col + 1
| _ -> failwith $"{dir} isn't a numpad direction."
let rowCount = arr |> Array2D.length1
let colCount = arr |> Array2D.length2
if nextRow >= 0 && nextRow < rowCount && nextCol >= 0 && nextCol < colCount
then isXmas nextRow nextCol dir (depth+1) arr
else false
else false
Then you run this for every appropriately pruned 'X' times every direction and count the trues.
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: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.