summary refs log tree commit diff stats
path: root/day20.fsx
blob: e357986cc850a4aac7f60155fc2cca9e63c72e37 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
open System.IO
open System.Text.RegularExpressions

type Vector =
    struct
        val mutable x: int
        val mutable y: int
        val mutable z: int
        new(X, Y, Z) = {x=X; y=Y; z=Z}

        member this.add(other: Vector) =
            this.x <- this.x + other.x
            this.y <- this.y + other.y
            this.z <- this.z + other.z
        
        member this.manhattan() =
            abs this.x + abs this.y + abs this.z
        
        override this.ToString() =
            sprintf "(%d, %d, %d)" this.x this.y this.z
    end

type Projectile =
    struct
        val id: int
        val mutable position: Vector
        val mutable velocity: Vector
        val acceleration:     Vector
        new(ID, pos, vel, acc) = {id=ID; position=pos; velocity=vel; acceleration=acc}

        member this.update() =
            this.velocity.add(this.acceleration)
            this.position.add(this.velocity)
            this

        member this.distance() =
            this.velocity.manhattan()

        override this.ToString() =
            sprintf "p=%A v=%A a=%A" this.position this.velocity this.acceleration
    end

let parseInput i str =
    let m = Regex("(-?\d+)").Matches(str)
    let values = [for g in m -> int g.Value]
    if List.length values <> 9 then printfn "%A" values; failwith "invalid input"
    Projectile(i, Vector(values[0], values[1], values[2]), 
                  Vector(values[3], values[4], values[5]), 
                  Vector(values[6], values[7], values[8]))

let part1() =
    let mutable projectiles = File.ReadAllLines "day20.txt" |> Array.mapi parseInput
    // assume that 1000 simulations is good enough
    for i = 1 to 1000 do
        projectiles <- Array.map (fun (x: Projectile) -> x.update()) projectiles
    Array.minBy (fun (x: Projectile) -> x.distance()) projectiles |> (fun x -> x.id) |> printfn "%d"

let part2() =
    let mutable projectiles = File.ReadAllLines "day20.txt" |> Array.mapi parseInput
    for i = 1 to 1000 do
        projectiles <- Array.map (fun (x: Projectile) -> x.update()) projectiles
        let uncollided = Array.groupBy (fun (x: Projectile) -> x.position) projectiles
                         |> Array.filter (fun (pos, xs) -> Array.length xs = 1)
                         |> Array.map snd
                         |> Array.fold Array.append Array.empty
        projectiles <- uncollided
    Array.length projectiles |> printfn "%d"

let () =
    part1()
    part2()