Skip to content

Commit f282d9e

Browse files
Various fix ups (#5)
* Add a --all command line option to run all days * Switch answer type from Any to an associated type, defaulting to Int * Switch to String(contentsOf:encoding:) * Clarify new day instructions
1 parent 454d588 commit f282d9e

File tree

3 files changed

+55
-20
lines changed

3 files changed

+55
-20
lines changed

README.md

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,23 @@ The challenges assume three files (replace 00 with the day of the challenge).
3333
- `Sources/Day00.swift`: the code to solve the challenge
3434
- `Tests/Day00.swift`: any unit tests that you want to include
3535

36-
To start a new day's challenge, make a copy of these files and update as
37-
necessary. The `AdventOfCode.swift` file controls which day's challenge is run
36+
To start a new day's challenge, make a copy of these files, updating 00 to the
37+
day number.
38+
39+
```diff
40+
// Add each new day implementation to this array:
41+
let allChallenges: [any AdventDay] = [
42+
- Day00()
43+
+ Day00(),
44+
+ Day01(),
45+
]
46+
```
47+
48+
Then implement part 1 and 2. The `AdventOfCode.swift` file controls which challenge
49+
is run with `swift run`. Add your new type to its `allChallenges` array. By default
50+
it runs the most recent challenge.
51+
52+
The `AdventOfCode.swift` file controls which day's challenge is run
3853
with `swift run`. By default that runs the most recent challenge in the package.
3954

4055
To supply command line arguments use `swift run AdventOfCode`. For example,

Sources/AdventDay.swift

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import Foundation
44

55
protocol AdventDay {
6+
associatedtype Answer = Int
7+
68
/// The day of the Advent of Code challenge.
79
///
810
/// You can implement this property, or, if your type is named with the
@@ -13,10 +15,15 @@ protocol AdventDay {
1315
init(data: String)
1416

1517
/// Computes and returns the answer for part one.
16-
func part1() async throws -> Any
18+
func part1() async throws -> Answer
1719

1820
/// Computes and returns the answer for part two.
19-
func part2() async throws -> Any
21+
func part2() async throws -> Answer
22+
}
23+
24+
struct PartUnimplemented: Error {
25+
let day: Int
26+
let part: Int
2027
}
2128

2229
extension AdventDay {
@@ -42,8 +49,8 @@ extension AdventDay {
4249

4350
// Default implementation of `part2`, so there aren't interruptions before
4451
// working on `part1()`.
45-
func part2() -> Any {
46-
"Not implemented yet"
52+
func part2() throws -> Answer {
53+
throw PartUnimplemented(day: day, part: 2)
4754
}
4855

4956
/// An initializer that loads the test data from the corresponding data file.
@@ -60,12 +67,12 @@ extension AdventDay {
6067
subdirectory: "Data")
6168

6269
guard let dataURL,
63-
let data = try? String(contentsOf: dataURL)
70+
let data = try? String(contentsOf: dataURL, encoding: .utf8)
6471
else {
6572
fatalError("Couldn't find file '\(dataFilename).txt' in the 'Data' directory.")
6673
}
6774

68-
// On Windows, line separators may be CRLF. Converting to LF so that \n
75+
// On Windows, line separators may be CRLF. Converting to LF so that \n
6976
// works for string parsing.
7077
return data.replacingOccurrences(of: "\r", with: "")
7178
}

Sources/AdventOfCode.swift

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ struct AdventOfCode: AsyncParsableCommand {
1313
@Flag(help: "Benchmark the time taken by the solution")
1414
var benchmark: Bool = false
1515

16+
@Flag(help: "Run all the days available")
17+
var all: Bool = false
18+
1619
/// The selected day, or the latest day if no selection is provided.
1720
var selectedChallenge: any AdventDay {
1821
get throws {
@@ -33,36 +36,46 @@ struct AdventOfCode: AsyncParsableCommand {
3336
allChallenges.max(by: { $0.day < $1.day })!
3437
}
3538

36-
func run(part: () async throws -> Any, named: String) async -> Duration {
37-
var result: Result<Any, Error> = .success("<unsolved>")
39+
func run<T>(part: () async throws -> T, named: String) async -> Duration {
40+
var result: Result<T, Error>?
3841
let timing = await ContinuousClock().measure {
3942
do {
4043
result = .success(try await part())
4144
} catch {
4245
result = .failure(error)
4346
}
4447
}
45-
switch result {
48+
switch result! {
4649
case .success(let success):
4750
print("\(named): \(success)")
51+
case .failure(let failure as PartUnimplemented):
52+
print("Day \(failure.day) part \(failure.part) unimplemented")
4853
case .failure(let failure):
4954
print("\(named): Failed with error: \(failure)")
5055
}
5156
return timing
5257
}
5358

5459
func run() async throws {
55-
let challenge = try selectedChallenge
56-
print("Executing Advent of Code challenge \(challenge.day)...")
60+
let challenges =
61+
if all {
62+
allChallenges
63+
} else {
64+
try [selectedChallenge]
65+
}
5766

58-
let timing1 = await run(part: challenge.part1, named: "Part 1")
59-
let timing2 = await run(part: challenge.part2, named: "Part 2")
67+
for challenge in challenges {
68+
print("Executing Advent of Code challenge \(challenge.day)...")
6069

61-
if benchmark {
62-
print("Part 1 took \(timing1), part 2 took \(timing2).")
63-
#if DEBUG
64-
print("Looks like you're benchmarking debug code. Try swift run -c release")
65-
#endif
70+
let timing1 = await run(part: challenge.part1, named: "Part 1")
71+
let timing2 = await run(part: challenge.part2, named: "Part 2")
72+
73+
if benchmark {
74+
print("Part 1 took \(timing1), part 2 took \(timing2).")
75+
#if DEBUG
76+
print("Looks like you're benchmarking debug code. Try swift run -c release")
77+
#endif
78+
}
6679
}
6780
}
6881
}

0 commit comments

Comments
 (0)