11//
2- // <#ChallengeName#> .swift
2+ // TreetopTreeHouse .swift
33//
4- // Created by Author on <#YYYY-MM-DD#> .
4+ // Created by Matthew Judy on 2021-01-04 .
55//
66
77
@@ -15,33 +15,201 @@ import Shared
1515
1616 # Part One
1717
18- <#Challenge Documentation#>
19- */
18+ The expedition comes across a peculiar patch of tall trees all planted
19+ carefully in a grid. The Elves explain that a previous expedition planted
20+ these trees as a reforestation effort. Now, they're curious if this would be a
21+ good location for a tree house.
22+
23+ First, determine whether there is enough tree cover here to keep a tree
24+ house **hidden**. To do this, you need to count the number of trees that are
25+ **visible from outside the grid** when looking directly along a row or column.
26+
27+ The Elves have already launched a quadcopter to generate a map with the height
28+ of each tree (your puzzle input). For example:
29+
30+ ```
31+ 30373
32+ 25512
33+ 65332
34+ 33549
35+ 35390
36+ ```
37+
38+ Each tree is represented as a single digit whose value is its height, where
39+ `0` is the shortest and `9` is the tallest.
40+
41+ A tree is **visible** if all of the other trees between it and an edge of the
42+ grid are **shorter** than it. Only consider trees in the same row or column;
43+ that is, only look up, down, left, or right from any given tree.
44+
45+ All of the trees around the edge of the grid are **visible** - since they are
46+ already on the edge, there are no trees to block the view. In this example,
47+ that only leaves the interior nine trees to consider:
48+
49+ The top-left 5 is visible from the left and top. (It isn't visible from the
50+ right or bottom since other trees of height 5 are in the way.)
51+
52+ * The top-middle 5 is visible from the top and right.
53+ * The top-right 1 is not visible from any direction; for it to be visible,
54+ there would need to only be trees of height 0 between it and an edge.
55+ * The left-middle 5 is visible, but only from the right.
56+ * The center 3 is not visible from any direction; for it to be visible, there
57+ would need to be only trees of at most height 2 between it and an edge.
58+ * The right-middle 3 is visible from the right.
59+ * In the bottom row, the middle 5 is visible, but the 3 and 4 are not.
60+
61+ With 16 trees visible on the edge and another 5 visible in the interior, a total of 21 trees are visible in this arrangement.
62+
63+ Consider your map; how many trees are visible from outside the grid?
64+
65+ # Part Two
66+
67+
68+ Content with the amount of tree cover available, the Elves just need to know
69+ the best spot to build their tree house: they would like to be able to see
70+ a lot of **trees**.
71+
72+ To measure the viewing distance from a given tree, look up, down, left, and
73+ right from that tree; stop if you reach an edge or at the first tree that is
74+ the same height or taller than the tree under consideration. (If a tree is
75+ right on the edge, at least one of its viewing distances will be zero.)
76+
77+ The Elves don't care about distant trees taller than those found by the rules
78+ above; the proposed tree house has large eaves to keep it dry, so they wouldn't
79+ be able to see higher than the tree house anyway.
80+
81+ In the example above, consider the middle `5` in the second row:
82+
83+ ```
84+ 30373
85+ 25512 <-- the centered `5`
86+ 65332
87+ 33549
88+ 35390
89+ ```
90+
91+ * Looking up, its view is not blocked; it can see `1` tree (of height `3`).
92+ * Looking left, its view is blocked immediately; it can see only `1` tree
93+ (of height `5`, right next to it).
94+ * Looking right, its view is not blocked; it can see `2` trees.
95+ * Looking down, its view is blocked eventually; it can see `2` trees
96+ (one of height `3`, then the tree of height `5` that blocks its view).
97+
98+ A tree's **scenic score** is found by **multiplying together** its viewing
99+ distance in each of the four directions. For this tree, this is `4`
100+ (found by multiplying `1 * 1 * 2 * 2`).
101+
102+ However, you can do even better: consider the tree of height `5` in the middle of the fourth row:
103+
104+ ```
105+ 30373
106+ 25512
107+ 65332
108+ 33549 <-- the centered `5`
109+ 35390
110+ ```
111+
112+ * Looking up, its view is blocked at `2` trees
113+ (by another tree with a height of 5).
114+ * Looking left, its view is not blocked; it can see `2` trees.
115+ * Looking down, its view is also not blocked; it can see `1` tree.
116+ * Looking right, its view is blocked at `2` trees
117+ (by a massive tree of height 9).
118+
119+ This tree's scenic score is `8` (`2 * 2 * 1 * 2`); this is the ideal spot for
120+ the tree house.
121+
122+ Consider each tree on your map. **What is the highest scenic score possible
123+ for any tree?**
124+ **/
20125@main
21- struct < #ChallengeName# > : ParsableCommand
126+ struct TreetopTreeHouse : AsyncParsableCommand
22127{
23- /// <# Enumeration for argument which activates "Part Two" behavior#>
128+ /// Enumeration for argument which activates "Part Two" behavior
24129 enum Mode : String , ExpressibleByArgument , CaseIterable
25130 {
26- case modeA
27- case modeB
131+ case visibleTreeCount
132+ case bestScenicScore
28133 }
29134
30- @Option ( help: " <#Argument used to activate “Part Two” behavior.#> " )
135+ @Option ( help: " 'visibleTreeCount' or 'bestScenicScore' " )
31136 var mode : Mode
32137}
33138
34139
140+ typealias Tree = ( height: Int , visible: Bool )
141+
142+
143+ extension Sequence where Element : Collection
144+ {
145+ subscript( column column : Element . Index ) -> [ Element . Iterator . Element ]
146+ {
147+ return map { $0 [ column ] }
148+ }
149+ }
150+
151+
35152// MARK: - Command Execution
36153
37- extension < #ChallengeName# >
154+ extension TreetopTreeHouse
38155{
39- mutating func run( ) throws
156+ func evaluateHeights< Result> ( rows: [ [ Int ] ] , columns: [ [ Int ] ] , edgeValue: Result , reduceInto startValue: Result , evaluation: ( ( _ result: Result , _ info: ( directionalTrees: [ Int ] , height: Int ) ) -> Result ) ) -> [ [ Result ] ]
157+ {
158+ return rows. enumerated ( ) . map
159+ {
160+ ( rowIndex, row) in
161+
162+ if ( ( rowIndex == 0 ) || ( rowIndex == rows. lastIndex) )
163+ {
164+ return Array ( repeating: edgeValue, count: columns. count)
165+ }
166+
167+ return row. enumerated ( ) . map
168+ {
169+ ( columnIndex, height) in
170+
171+ if ( ( columnIndex == 0 ) || ( columnIndex == columns. lastIndex) )
172+ {
173+ return edgeValue
174+ }
175+
176+ // We're now evaluating an "inner" tree, which may not be visible.
177+ // Look around and evaluate based on our surroundings.
178+ let column = columns [ columnIndex]
179+ return [
180+ ( Array < Int > ( row [ ..< columnIndex] . reversed ( ) ) , height) , // leftward
181+ ( Array < Int > ( row [ columnIndex... ] . dropFirst ( ) ) , height) , // rightward
182+ ( Array < Int > ( column [ ..< rowIndex] . reversed ( ) ) , height) , // upward
183+ ( Array < Int > ( column [ rowIndex... ] . dropFirst ( ) ) , height) , // downward
184+ ] . reduce ( startValue, evaluation)
185+ }
186+ }
187+ }
188+
189+
190+ mutating func run( ) async throws
40191 {
41- while let inputLine = readLine ( )
192+ let input = FileHandle . standardInput. bytes
193+ let rows : [ [ Int ] ] = try await input. lines. reduce ( into: [ [ Int] ] ( ) ) { $0. append ( $1. compactMap { Int ( String ( $0) ) ! } ) }
194+ let columns : [ [ Int ] ] = rows [ 0 ] . enumerated ( ) . map { rows [ column: $0. 0 ] }
195+
196+ switch self . mode
42197 {
43- print ( " \( inputLine) " )
198+ case . visibleTreeCount:
199+ let visibilityGrid = self . evaluateHeights ( rows: rows, columns: columns, edgeValue: true , reduceInto: false )
200+ {
201+ result, info in
202+ return ( result || ( info. directionalTrees. firstIndex { $0 >= info. height } == nil ) )
203+ }
204+ print ( visibilityGrid. reduce ( 0 ) { ( $0 + $1. count { $0 } ) } )
205+
206+ case . bestScenicScore:
207+ let scoreGrid = self . evaluateHeights ( rows: rows, columns: columns, edgeValue: 0 , reduceInto: 1 )
208+ {
209+ result, info in
210+ return ( result * ( ( info. directionalTrees. firstIndex { $0 >= info. height } ?? ( info. directionalTrees. count - 1 ) ) + 1 ) )
211+ }
212+ print ( scoreGrid. joined ( ) . max ( ) !)
44213 }
45214 }
46215}
47-
0 commit comments