Advent of Code 2022: Day 14

For this, I used a table data-structure to hold the cave definition, using a default value of "air". The implementation of the problem is then relatively straightforward, with no new language features required.

Note that definitions of max and min are required for Icon 9.5.1.

Final Program

global cave, cave_base

procedure main(inputs)
  local filename

  if *inputs = 1 then {
    filename := inputs[1]

    # Part 1 solution
    write("Part 1 solution is: ", part_1(filename))
    # Part 2 solution
    write("Part 2 solution is: ", part_2(filename))
  } else {
    write("Provide a filename of data")
  }
end

procedure part_1(filename)
  local num_settled

  load_data(filename)

  num_settled := 0
  while drop_sand(500, 0) do
    num_settled +:= 1

  return num_settled
end

procedure part_2(filename)
  local num_settled

  load_data(filename)

  num_settled := 0
  while cave[500][0] == "." do {
    drop_sand(500, 0, "with floor")
    num_settled +:= 1
  }

  return num_settled
end

# Recursive procedure, repeats until sand settles or drops below cave_base
procedure drop_sand(x, y, with_floor)
  if /with_floor then { # if no floor, falls into void
    if y > cave_base then fail
  } else { # settles on floor
    if y = cave_base+1 then {
      cave_set(x, y, "o")
      return
    }
  }

  if cave[x][y+1] == "." then
    return drop_sand(x, y+1, with_floor)
  else if cave[x-1, y+1] == "." then
    return drop_sand(x-1, y+1, with_floor)
  else if cave[x+1, y+1] == "." then
    return drop_sand(x+1, y+1, with_floor)
  else {
    cave_set(x, y, "o")
    return
  }
end

procedure load_data(filename)
  local file, from_x, from_y, line, to_x, to_y

  file := open(filename) | stop("Cannot open ", filename)
  cave := table(table("."))

  every line := !file do {
    line ? {
      from_x := integer(tab(many(&digits)))
      =","
      from_y := integer(tab(many(&digits)))
      while (=" -> ") do {
        to_x := integer(tab(many(&digits)))
        =","
        to_y := integer(tab(many(&digits)))
        add_wall(from_x, from_y, to_x, to_y)
        from_x := to_x
        from_y := to_y
      }
    }
  }
  close(file)
end

# Adds a wall horizontally or vertically
procedure add_wall(from_x, from_y, to_x, to_y)
  local i

  /cave_base := from_y
  cave_base := max(max(cave_base, from_y), to_y)

  if from_x = to_x then {
    every i := min(from_y,to_y) to max(from_y,to_y) do {
      cave_set(from_x, i, "#")
    }
  } else {
    every i := min(from_x,to_x) to max(from_x,to_x) do {
      cave_set(i, from_y, "#")
    }
  }
end

# Ensure there is a table for cave[x] before setting a value
procedure cave_set(x, y, value)
  if not member(cave, x) then cave[x] := table(".")
  cave[x][y] := value
end

procedure max(a, b)
  if a < b then
    return b
  else
    return a
end

procedure min(a, b)
  if a < b then
    return a
  else
    return b
end

Page from Peter's Scrapbook, output from a VimWiki on 2024-01-29.