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
disk_map = DATA.read.chars.each_slice(2)
  .flat_map.with_index {|(a,b),i|
    [ Array.new(a.to_i, i), Array.new(b.to_i, nil) ]
  }.reject(&:empty?)

# while (prev, free = disk_map.each_cons(2).find { _2.include?(nil) })
#   tail = disk_map.last
#   until free.empty? || tail.empty?
#     prev << tail.shift
#     free.pop
#   end
#
#   while disk_map.last.all?(&:nil?)
#     disk_map.pop
#   end
# end

# pp disk_map.flatten.compact.each.with_index.sum { _1 * _2 }

Block = Struct.new(:id, :size, :prev, :next) do
  def each(dir: :next, stop: nil)
    return enum_for(__method__, dir:, stop:) unless block_given?

    cur = self
    while cur != stop
      yield cur
      cur = cur.send(dir)
    end
  end

  def inspect
    "<Block id=#{id} size=#{size} prev=#{prev&.id} next=#{self.next&.id}>"
  end

  def to_s
    (id || ?.).to_s * size
  end
end

blocks = disk_map.map { Block.new(_1[0], _1.size) }
  .each_cons(2) { _1.next = _2; _2.prev = _1 }

head = blocks.first

blocks.last.each(dir: :prev).reject { _1.id.nil? }.each do |file|
  free = head.each(stop: file)
    .select { _1.id.nil? }
    .find { _1.size >= file.size }
  next if free.nil?

  free.id, file.id = file.id, free.id

  if free.size > file.size
    c = Block.new(nil, free.size - file.size, free, free.next)
    free.size = file.size
    free.next = c
    c.next.prev = c
  end
end

pp head.each.flat_map { Array.new(_1.size, _1.id) }.each.with_index.sum { _1.to_i * _2 }

__END__