Skip to content

Commit d638e84

Browse files
author
Matthew Judy
committed
Complete challenge 2023-02.
1 parent 39a703c commit d638e84

File tree

4 files changed

+231
-9
lines changed

4 files changed

+231
-9
lines changed

Package.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ let package: Package = Package(
2929

3030
targets: [
3131
.target(name: "Shared"),
32+
33+
// 2022
3234
.executableTarget(name: "calorie-counting", dependencies: commonDependencies, path: "Sources/2022/01", swiftSettings: commonSettings),
3335
.executableTarget(name: "rock-paper-scissors", dependencies: commonDependencies, path: "Sources/2022/02", swiftSettings: commonSettings),
3436
.executableTarget(name: "rucksack-reorganization", dependencies: commonDependencies, path: "Sources/2022/03", swiftSettings: commonSettings),
@@ -37,6 +39,9 @@ let package: Package = Package(
3739
.executableTarget(name: "tuning-trouble", dependencies: commonDependencies, path: "Sources/2022/06", swiftSettings: commonSettings),
3840
.executableTarget(name: "no-space-left-on-device", dependencies: commonDependencies, path: "Sources/2022/07", swiftSettings: commonSettings),
3941
.executableTarget(name: "treetop-tree-house", dependencies: commonDependencies, path: "Sources/2022/08", swiftSettings: commonSettings),
40-
.executableTarget(name: "trebuchet", dependencies: commonDependencies, path: "Sources/2023/01", swiftSettings: commonSettings),
42+
43+
// 2023
44+
.executableTarget(name: "trebuchet", dependencies: commonDependencies, path: "Sources/2023/01", swiftSettings: commonSettings),
45+
.executableTarget(name: "cube-conundrum", dependencies: commonDependencies, path: "Sources/2023/02", swiftSettings: commonSettings),
4146
]
4247
)

Sources/2023/01/Trebuchet.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ extension Trebuchet
111111

112112
mutating func run() async throws
113113
{
114-
let input: AsyncLineSequence = FileHandle.standardInput.bytes.lines // URL.homeDirectory.appending(path: "Desktop/input.txt").lines
114+
let input: AsyncLineSequence = FileHandle.standardInput.bytes.lines
115115
let calibrationSum: Int = try await input.reduce(0)
116116
{
117117
currentSum, inputLine in

Sources/2023/02/CubeConundrum.swift

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
//
2+
// CubeConundrum.swift
3+
//
4+
// Created by Matthew Judy on 2023-12-05.
5+
//
6+
7+
8+
import ArgumentParser
9+
import Foundation
10+
import Shared
11+
12+
13+
/// Day 2 : Cube Conundrum
14+
///
15+
/// # Part One
16+
///
17+
/// You're launched high into the atmosphere! The apex of your trajectory just
18+
/// barely reaches the surface of a large island floating in the sky.
19+
/// You gently land in a fluffy pile of leaves. It's quite cold, but you don't
20+
/// see much snow. An Elf runs over to greet you.
21+
///
22+
/// The Elf explains that you've arrived at **Snow Island** and apologizes for
23+
/// the lack of snow. He'll be happy to explain the situation, but it's a bit
24+
/// of a walk, so you have some time. They don't get many visitors up here;
25+
/// would you like to play a game in the meantime?
26+
///
27+
/// As you walk, the Elf shows you a small bag and some cubes which are either
28+
/// red, green, or blue. Each time you play this game, he will hide a secret
29+
/// number of cubes of each color in the bag, and your goal is to figure out
30+
/// information about the number of cubes.
31+
///
32+
/// To get information, once a bag has been loaded with cubes, the Elf will
33+
/// reach into the bag, grab a handful of random cubes, show them to you, and
34+
/// then put them back in the bag. He'll do this a few times per game.
35+
///
36+
/// You play several games and record the information from each game (your
37+
/// puzzle input). Each game is listed with its ID number (like the `11` in
38+
/// `Game 11: ...`) followed by a semicolon-separated list of subsets of cubes
39+
/// that were revealed from the bag (like `3 red, 5 green, 4 blue`).
40+
///
41+
/// For example, the record of a few games might look like this:
42+
///
43+
/// ```
44+
/// Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green
45+
/// Game 2: 1 blue, 2 green; 3 green, 4 blue, 1 red; 1 green, 1 blue
46+
/// Game 3: 8 green, 6 blue, 20 red; 5 blue, 4 red, 13 green; 5 green, 1 red
47+
/// Game 4: 1 green, 3 red, 6 blue; 3 green, 6 red; 3 green, 15 blue, 14 red
48+
/// Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green
49+
/// ```
50+
///
51+
/// In game 1, three sets of cubes are revealed from the bag (and then put back
52+
/// again). The first set is 3 blue cubes and 4 red cubes; the second set is
53+
/// 1 red cube, 2 green cubes, and 6 blue cubes; the third set is only
54+
/// 2 green cubes.
55+
///
56+
/// The Elf would first like to know which games would have been possible if
57+
/// the bag contained **only 12 red cubes, 13 green cubes, and 14 blue cubes**?
58+
///
59+
/// In the example above, games 1, 2, and 5 would have been **possible** if
60+
/// the bag had been loaded with that configuration. However, game 3 would have
61+
/// been **impossible** because at one point the Elf showed you 20 red cubes
62+
/// at once; similarly, game 4 would also have been impossible because the Elf
63+
/// showed you 15 blue cubes at once. If you add up the IDs of the games that
64+
/// would have been possible, you get **`8`**.
65+
///
66+
/// Determine which games would have been possible if the bag had been loaded
67+
/// with only 12 red cubes, 13 green cubes, and 14 blue cubes. **What is the
68+
/// sum of the IDs of those games?**
69+
///
70+
/// # Part Two
71+
///
72+
/// The Elf says they've stopped producing snow because they aren't getting any
73+
/// **water**! He isn't sure why the water stopped; however, he can show you
74+
/// how to get to the water source to check it out for yourself. It's just
75+
/// up ahead!
76+
///
77+
/// As you continue your walk, the Elf poses a second question: in each game
78+
/// you played, what is the **fewest number of cubes of each color** that could
79+
/// have been in the bag to make the game possible?
80+
///
81+
/// Again consider the example games from earlier.
82+
///
83+
/// * In game 1, the game could have been played with as few as 4 red, 2 green,
84+
/// and 6 blue cubes. If any color had even one fewer cube, the game would
85+
/// have been impossible.
86+
/// * Game 2 could have been played with a minimum of 1 red, 3 green, and
87+
/// 4 blue cubes.
88+
/// * Game 3 must have been played with at least 20 red, 13 green, and
89+
/// 6 blue cubes.
90+
/// * Game 4 required at least 14 red, 3 green, and 15 blue cubes.
91+
/// * Game 5 needed no fewer than 6 red, 3 green, and 2 blue cubes in the bag.
92+
///
93+
/// The **power** of a set of cubes is equal to the numbers of red, green,
94+
/// and blue cubes multiplied together. The power of the minimum set of cubes
95+
/// in game 1 is `48`. In games 2-5 it was `12`, `1560`, `630`, and `36`,
96+
/// respectively. Adding up these five powers produces the sum `2286`.
97+
///
98+
/// For each game, find the minimum set of cubes that must have been present.
99+
/// **What is the sum of the power of these sets?**
100+
///
101+
@main
102+
struct CubeConundrum: AsyncParsableCommand
103+
{
104+
/// Enumeration for argument which activates "Part Two" behavior
105+
enum Mode: String, ExpressibleByArgument, CaseIterable
106+
{
107+
case sumOfGameIDs
108+
case sumOfGamePowers
109+
}
110+
111+
@Option(help: "'sumOfGameIDs' or 'sumOfGamePowers'")
112+
var mode: Mode = .sumOfGamePowers
113+
}
114+
115+
116+
struct Grab
117+
{
118+
let redCubes : Int
119+
let greenCubes : Int
120+
let blueCubes : Int
121+
122+
func isPossible(for loadout: Grab) -> Bool
123+
{
124+
[\Grab.redCubes, \Grab.greenCubes, \Grab.blueCubes]
125+
.reduce(true) {
126+
$0 && (loadout[keyPath: $1] >= self[keyPath: $1])
127+
}
128+
}
129+
}
130+
131+
132+
extension Grab
133+
{
134+
/// Initialize with segment of input line
135+
/// - Parameter input: input line representing a single grab,
136+
/// e.g., `1 green, 3 red, 6 blue`
137+
init(input: String) throws
138+
{
139+
var inputRed = 0
140+
var inputGreen = 0
141+
var inputBlue = 0
142+
143+
try input.components(separatedBy: ", ").forEach
144+
{
145+
guard case let gemInfo = $0.components(separatedBy: " "), (gemInfo.count == 2),
146+
let gemCount: Int = Int(gemInfo[0]),
147+
case let gemColor: String = gemInfo[1]
148+
else { throw AteShit(whilst: .parsing, input) }
149+
150+
switch gemColor
151+
{
152+
case "red" : inputRed = gemCount
153+
case "green" : inputGreen = gemCount
154+
case "blue" : inputBlue = gemCount
155+
default: throw AteShit(whilst: .parsing, input)
156+
}
157+
}
158+
159+
self = Grab(redCubes: inputRed, greenCubes: inputGreen, blueCubes: inputBlue)
160+
}
161+
}
162+
163+
164+
struct Game
165+
{
166+
let id : Int
167+
let grabs : [Grab]
168+
169+
var maxRed : Int { self.grabs.map(\.redCubes) .max() ?? 0 }
170+
var maxGreen : Int { self.grabs.map(\.greenCubes).max() ?? 0 }
171+
var maxBlue : Int { self.grabs.map(\.blueCubes) .max() ?? 0 }
172+
var minLoadout : Grab { Grab(redCubes: self.maxRed, greenCubes: self.maxGreen, blueCubes: self.maxBlue ) }
173+
174+
func isPossible(for loadout: Grab) -> Bool
175+
{
176+
self.grabs.first { $0.isPossible(for: loadout) == false } == nil
177+
}
178+
179+
/// Initialize with a full input line
180+
/// - Parameter input: input line representing a full game with multiple
181+
/// grabs, e.g., `Game 5: 6 red, 1 blue, 3 green; 2 blue, 1 red, 2 green`
182+
init(input: String) throws
183+
{
184+
guard case let inputSections : [String] = input.components(separatedBy: ": "), (inputSections.count == 2),
185+
let gameID : Int = Int(inputSections[0].components(separatedBy: " ")[1]),
186+
case let grabInputs : [String] = inputSections[1].components(separatedBy: "; "), (grabInputs.count > 0)
187+
else { throw AteShit(whilst: .parsing, input) }
188+
189+
self.id = gameID
190+
self.grabs = try grabInputs.reduce(into: []) { $0.append(try Grab(input: $1)) }
191+
}
192+
}
193+
194+
// MARK: - Command Execution
195+
196+
extension CubeConundrum
197+
{
198+
mutating func run() async throws
199+
{
200+
let input : AsyncLineSequence = FileHandle.standardInput.bytes.lines
201+
let loadout : Grab = Grab(redCubes: 12, greenCubes: 13, blueCubes: 14)
202+
let output : Int = try await input.reduce(0)
203+
{
204+
let game : Game = try Game(input: $1)
205+
206+
switch self.mode
207+
{
208+
case .sumOfGameIDs:
209+
return if game.isPossible(for: loadout) { $0 + game.id } else { $0 }
210+
211+
case .sumOfGamePowers:
212+
return ( $0 + (game.minLoadout.redCubes * game.minLoadout.greenCubes * game.minLoadout.blueCubes) )
213+
}
214+
}
215+
216+
print(output)
217+
}
218+
}
219+

Template/ChallengeName.swift

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,11 @@ import Foundation
1010
import Shared
1111

1212

13-
/**
14-
Day <#Day Number#> : <#Challenge Name#>
15-
16-
# Part One
17-
18-
<#Challenge Documentation#>
19-
*/
13+
/// Day <#Day Number#> : <#Challenge Name#>
14+
///
15+
/// # Part One
16+
///
17+
/// <#Challenge Documentation#>
2018
@main
2119
struct <#ChallengeName#>: AsyncParsableCommand
2220
{

0 commit comments

Comments
 (0)