DEV Community

Discussion on: Advent of Code 2020 Solution Megathread - Day 17: Conway Cubes

Collapse
 
shalvah profile image
Shalvah • Edited

Ruby solution. It's an eternity of eachs (Ruby's for loop), but it runs in a few seconds.

Today's main problem was the example. Instructions were pretty clear, but the example had me thinking I misunderstood. Had to check Reddit, where a lot of other folks were confused too, and realised I was correct, the example had just been cropped weirdly.

$actives = {0 => {0 => {}}}
x_bounds = {max: 0, min: 0}
y_bounds = {max: 0, min: 0}
z_bounds = {max: 0, min: 0}
w_bounds = {max: 0, min: 0}

File.readlines("input.txt").each_with_index do |line, y|
  line.each_char.with_index do |char, x|
    if char == "#"
      $actives[0][0][y] ||= {}
      $actives[0][0][y][x] = true

      x_bounds[:max] = x if x > x_bounds[:max]
      x_bounds[:min] = x if x < x_bounds[:min]
    end
  end
  y_bounds[:max] = y if y > y_bounds[:max]
  y_bounds[:min] = y if y < y_bounds[:min]
end

def get_cube_status(x, y, z, w)
  if ($actives[w][z][y][x] rescue false) == true
    :active
  else
    :inactive
  end
end

def get_active_neighbours(x, y, z, w)
  active_neighbours = []
  (w - 1..w + 1).each do |current_w|
    (z - 1..z + 1).each do |current_z|
      (y - 1..y + 1).each do |current_y|
        (x - 1..x + 1).each do |current_x|
          next if [x, y, z, w] == [current_x, current_y, current_z, current_w]

          if get_cube_status(current_x, current_y, current_z, current_w) == :active
            active_neighbours << [current_x, current_y, current_z, current_w]
          end
        end
      end
    end
  end
  active_neighbours
end


6.times do
  w_bounds[:max] += 1
  w_bounds[:min] -= 1
  z_bounds[:max] += 1
  z_bounds[:min] -= 1
  x_bounds[:max] += 1
  x_bounds[:min] -= 1
  y_bounds[:max] += 1
  y_bounds[:min] -= 1

  next_state = Marshal.load(Marshal.dump($actives))
  (w_bounds[:min]..w_bounds[:max]).each do |w|
    (z_bounds[:min]..z_bounds[:max]).each do |z|
      (y_bounds[:min]..y_bounds[:max]).each do |y|
        (x_bounds[:min]..x_bounds[:max]).each do |x|
          cube_status = get_cube_status(x, y, z, w)
          active_neighbours = get_active_neighbours(x, y, z, w)
          case cube_status
          when :active
            next_state[w][z][y][x] = nil unless (active_neighbours.size == 2 || active_neighbours.size == 3)
          when :inactive
            if active_neighbours.size == 3
              next_state[w] ||= {}
              next_state[w][z] ||= {}
              next_state[w][z][y] ||= {}
              next_state[w][z][y][x] = true
            end
          end
        end
      end
    end
  end
  $actives = next_state
end


def count_active(actives, x_bounds, y_bounds, z_bounds, w_bounds)
  active_count = 0
  (w_bounds[:min]..w_bounds[:max]).each do |w|
    (z_bounds[:min]..z_bounds[:max]).each do |z|
      (y_bounds[:min]..y_bounds[:max]).each do |y|
        (x_bounds[:min]..x_bounds[:max]).each do |x|
          active = (actives[w][z][y][x] rescue false) == true
          active_count += 1 if active
        end
      end
    end
  end
  active_count
end

# Helper function for when you need to visualise the layout
def print_grid(actives, x_bounds, y_bounds, z_bounds, w_bounds)
  (w_bounds[:min]..w_bounds[:max]).each do |w|
    (z_bounds[:min]..z_bounds[:max]).each do |z|
      print "z = #{z}, w = #{w}\n"
      (y_bounds[:min]..y_bounds[:max]).each do |y|
        (x_bounds[:min]..x_bounds[:max]).each do |x|
          active = (actives[z][y][x] rescue false) == true
          print(active ? '#' : '.')
        end
        print "\n"
      end
      print "\n"
    end
    print "\n\n"
  end
end

# print_grid($actives, x_bounds, y_bounds, z_bounds, w_bounds)
p count_active($actives, x_bounds, y_bounds, z_bounds, w_bounds)
Enter fullscreen mode Exit fullscreen mode