Skip to content

Commit b817b76

Browse files
author
Matthew Judy
committed
Add 2022-08 solution, now completed.
1 parent 6fb97e8 commit b817b76

File tree

1 file changed

+182
-14
lines changed

1 file changed

+182
-14
lines changed

Sources/2022/08/TreetopTreeHouse.swift

Lines changed: 182 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
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

Comments
 (0)