Advent of Code 2022: Day 8
The basic datastructure for this problem is a 2d grid of tree heights: these are read from a file as consecutive rows of strings.
Icon lets us index into a list and strings using the same
notation, so forest[1][2]
would mean the second character in the
first string of the forest list.
Unicon-only: this representation is easily read in using a list comprehension:
forest := [: !file :]
The items returned by the generator are used to construct the list.
The other Icon point worth mentioning is the to
operator:
-
1 to 10
is a generator for all items from 1 to 10, inclusive -
10 to 1 by -2
will generate items backwards, with a difference of 2procedure main(arglist) every i := 1 to 10 do write (i) every i := 10 to 1 by -2 do write (i) end
Output:
> unicon -s test.icn -x 1 2 3 4 5 6 7 8 9 10 10 8 6 4 2
Otherwise, the program to solve the problem is constrained by the logic of following the lines of sight along the four axes. Separate procedures are written to manage this logic along the x or y dimensions, separately.
Final Program
procedure main(inputs) local filename if *inputs = 1 then { filename := inputs[1] # Part 1 solution write("Part 1: ", part_1(filename)) # Part 2 solution write("Part 2: ", part_2(filename)) } else { write("Provide a filename of data") } end procedure part_1(filename) local forest, num_visible_trees local i, j forest := read_dataset(filename) num_visible_trees := 0 every i := 1 to *forest do every j := 1 to *forest[i] do if is_visible(forest, i, j) then num_visible_trees +:= 1 return num_visible_trees end procedure part_2(filename) local forest, current_scenic_score, most_scenic local i, j forest := read_dataset(filename) most_scenic := 0 every i := 2 to *forest-1 do { every j := 2 to *forest[i]-1 do { current_scenic_score := find_scenic_score(forest, i, j) if current_scenic_score > most_scenic then most_scenic := current_scenic_score } } return most_scenic end # Read the given dataset in, return list of strings procedure read_dataset(filename) local file, forest file := open(filename) | stop ("Cannot open file") forest := list() every put(forest, !file) close(file) return forest end # Succeeds if tree at [x, y] is visible, else fails. # Tree is visible if height larger in one of cardinal directions, or on edge procedure is_visible(forest, x, y) return x = 1 | y = 1 | x = *forest | y = *forest[x] | is_visible_x(forest, x, y, 1, x-1) | is_visible_x(forest, x, y, x+1, *forest) | is_visible_y(forest, x, y, 1, y-1) | is_visible_y(forest, x, y, y+1, *forest[x]) end procedure is_visible_x(forest, x, y, start_x, end_x) local highest_tree, x_side highest_tree := 0 every x_side := start_x to end_x do if forest[x_side][y] > highest_tree then highest_tree := forest[x_side][y] return highest_tree < forest[x][y] end procedure is_visible_y(forest, x, y, start_y, end_y) local highest_tree, y_side highest_tree := 0 every y_side := start_y to end_y do if forest[x][y_side] > highest_tree then highest_tree := forest[x][y_side] return highest_tree < forest[x][y] end # Scenic score is product of visible distances in each direction procedure find_scenic_score(forest, x, y) return scenic_score_x(forest, x, y, 1, -1) * scenic_score_x(forest, x, y, *forest, 1) * scenic_score_y(forest, x, y, 1, -1) * scenic_score_y(forest, x, y, *forest[x], 1) end procedure scenic_score_x(forest, x, y, end_x, offset) local score, try_x if x = end_x then return 1 score := 0 every try_x := x+offset to end_x by offset do { score +:= 1 if forest[try_x][y] >= forest[x][y] then return score } return score end procedure scenic_score_y(forest, x, y, end_y, offset) local score, try_y if y = end_y then return 1 score := 0 every try_y := y+offset to end_y by offset do { score +:= 1 if forest[x][try_y] >= forest[x][y] then return score } return score end