Advent of Code 2024 Day8

Today’s question is easy to write in mutable languages, like Python, but hard to write in FP code.

Let me know if you have some ideas to simplify my F# code.

Let’s improve our F# skills together! :D

type Day8(lines: string[]) =
    let R = lines.Length
    let C = lines[0].Length

    let getPoints (lines: string[]) : Map<char, Index array> =
        let points =
            [| for i in 0 .. R - 1 do
                   for j in 0 .. C - 1 do
                       if lines[i][j] <> '.' then
                           (lines[i][j], (i, j)) |]

        points
        |> Array.groupBy fst
        |> Map.ofArray
        |> Map.map (fun _ v -> v |> Array.map snd)


    let inBoard (i, j) : bool = 0 <= i && i < R && 0 <= j && j < C

    let getIdxPair (idx_list: Index array) =
        [ for i in 0 .. idx_list.Length - 2 do
              for j in i + 1 .. idx_list.Length - 1 do
                  (idx_list[i], idx_list[j]) ]

    let getAntinodes op (a: Index) (b: Index) : Index list =
        let ar, ac = a
        let br, bc = b

        let x = abs (ar - br)
        let y = abs (ac - bc)

        match ar < br, ac < bc with
        | true, true -> (-x, -y), (x, y)
        | true, false -> (-x, y), (x, -y)
        | false, true -> (x, -y), (-x, y)
        | false, false -> (x, y), (-x, -y)
        |> fun (d1, d2) -> op a d1 @ op b d2

    member this.Q1() =
        let points_dict = lines |> getPoints
        let getAntIdx (ar, ac) (dr, dc) = [ (ar + dr, ac + dc) ]

        points_dict.Values
        |> Array.ofSeq
        |> Seq.collect (fun idx_list ->
            idx_list
            |> getIdxPair
            |> List.collect (fun (l, r) -> getAntinodes getAntIdx l r))
        |> Seq.filter inBoard
        |> Set.ofSeq
        |> Seq.length

    member this.Q2() =
        let points_dict = lines |> getPoints

        let getAntinodesInLine (ar, ac) (x, y) =
            let rec loop (ar, ac) x y rst =
                if not (inBoard (ar, ac)) then
                    rst
                else
                    loop (ar + x, ac + y) x y ((ar, ac) :: rst)

            loop (ar, ac) x y []

        points_dict.Values
        |> Array.ofSeq
        |> Seq.collect (fun idx_list ->
            idx_list
            |> getIdxPair
            |> List.collect (fun (l, r) -> getAntinodes getAntinodesInLine l r))
        |> Set.ofSeq
        |> Seq.length

You can find the source code at https://github.com/zangruizhe/AoC

Advent of Code 2024 Day6

Tips for Q2:

If the Guard moving to the same direction at same position twice, that means Guard is in a loop.

type Day6(lines: string[]) =
    let R = lines.Length
    let C = lines[0].Length

    let getStart (lines: string[]) =
        lines
        |> Array.indexed
        // |> Array.pick (fun (i, s) -> s |> Seq.tryFindIndex ((=) '^') |> Option.map (fun j -> (i, j)))
        |> Array.pick (fun (i, s) ->
            let j = s.IndexOf("^")
            if j <> -1 then Some(i, j) else None)

    let ops = [ (-1, 0); (0, 1); (1, 0); (0, -1) ]

    let rec moving i j op (pos_set: HashSet<Index>) =
        pos_set.Add((i, j)) |> ignore
        let next_i = i + fst ops[op]
        let next_j = j + snd ops[op]

        if next_i < 0 || next_i >= R || next_j < 0 || next_j >= C then
            pos_set
        elif lines[next_i][next_j] = '#' then
            moving i j ((op + 1) % 4) pos_set
        else
            moving next_i next_j op pos_set


    member this.Q1() =
        let start: Index = getStart lines
        let post_set = moving (fst start) (snd start) 0 (HashSet())
        post_set.Count

    member this.Q2() =
        let start: Index = getStart lines
        let pos_set = moving (fst start) (snd start) 0 (HashSet())
        pos_set.Remove(start) |> ignore
        let isInLoop (pos_set: HashSet<int * int * int>) i j op = pos_set.Add((i, j, op)) = false

        let rec checkLoop i j op (pos_set: HashSet<int * int * int>) =

            let next_i = i + fst ops[op]
            let next_j = j + snd ops[op]

            if next_i < 0 || next_i >= R || next_j < 0 || next_j >= C then
                false
            elif lines[next_i][next_j] = '#' then
                if isInLoop pos_set i j op then
                    true
                else
                    checkLoop i j ((op + 1) % 4) pos_set
            else
                checkLoop next_i next_j op pos_set


        pos_set
        |> Seq.filter (fun (i, j) ->
            let old = lines[i]
            let str = old.ToCharArray()
            str[j] <- '#'
            lines[i] <- String(str)
            let rst = checkLoop (fst start) (snd start) 0 (HashSet())
            lines[i] <- old
            rst)
        |> Seq.length

You can find the source code at https://github.com/zangruizhe/AoC

Advent of Code 2024 Day3

Today’s question should process the whole input together.

If you process input line by line, you can pass Q1, but can not pass Q2

module Day3 =
    let Q1 (lines: string[]) =
        let str = String.Concat(lines)
        str |> split2IntByReg "mul\((\d+),(\d+)\)" |> Array.sumBy (fun n -> n[0] * n[1])

    let Q2 (lines: string[]) =
        let str = String.Concat(lines)

        Regex.Matches(str, @"mul\((\d+),(\d+)\)|don't\(\)|do\(\)")
        |> Seq.fold
            (fun (l, isEnable) m ->
                if m.Value.StartsWith("mul") then
                    if isEnable then
                        l @ [ (int m.Groups[1].Value, int m.Groups[2].Value) ], isEnable
                    else
                        l, isEnable
                elif m.Value = "do()" then
                    l, true
                elif m.Value = "don't()" then
                    l, false
                else
                    failwith $"wrong patten m={m.Value}")
            ([], true)
        |> fst
        |> Seq.sumBy (fun (l, r) -> l * r)

You can find the source code at https://github.com/zangruizhe/AoC

《让人生停止灰暗的艺术》

认知疗法

最重要的就是有爱的能力,爱的能力是发挥创造力,和享受生活的关键

在任何情况下,我们都可以为自己和为他人做些有意义的事情

思想实验–“流浪汉”疗法,思考自己是个一无所有的流浪汉,但是仍然能快乐生活,想想是否可以实现。

对生活的事情分级

(-3)worst/worse/bad/normal/good/better/best (3)

(-3)糟透了/很糟/糟/一般/棒/很棒/棒极了 (3)

先想想标尺的两头是什么:

糟透了的事情:死亡,恶性肿瘤

棒极了的事情:孩子出生,结婚

然后用这两头的事情来校准其他事情, 比如:

拿到了心仪的工作机会:很棒

没拿到心仪的工作机会:糟

丢掉了工作:糟

亏了10万:bad-normal(-0.5)

赚了20万:棒

通过这样校准,让我们对事物,尤其是糟糕的事物评价变得具体,从而不会轻易把事情归类到 糟透了/很糟 这个范围,也就不会这么担忧了

那些坚强、成熟的人,对生活中遇到的大多数事情都会归类到 【糟,一般、棒】这个范围内,所以他们的心态也就更加平和

认知练习

  • 事情:发生了什么事
  • 分级:事情的分级
  • 详情:事情的客观详情
  • 结果:过一段时间再回头看这个分级是否合理