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
map = DATA.readlines(chomp: true).flat_map.with_index {|row, y|
  row.chars.map.with_index {|height, x|
    [[y,x], height == ?. ? nil : height.to_i]
  }
}.to_h

trailheads = map.filter_map { _2 == 0 && _1}

DELTAS = [[-1, 0], [1, 0], [0, -1], [0, 1]]

def trails(map, head)
  active = [[head]]
  finished = []
  until active.empty?
    current = active.shift

    steps = DELTAS
      .map {|d| current.last.zip(d).map { _1 + _2 } }
      .select { map.fetch(_1, nil) == map.fetch(current.last) + 1 }

    if steps.empty?
      finished << current
    else
      active.concat(steps.map { current + [_1] })
    end
  end

  finished
    .select { map.fetch(_1.last) == 9 }
    .uniq
end

# part one
pp trailheads.sum { trails(map, _1).map(&:last).uniq.length }

# part two
pp trailheads.sum { trails(map, _1).length }

__END__