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.
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