Skip to content

Commit

Permalink
Various fix ups (#5)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
airspeedswift authored Dec 7, 2023
1 parent 454d588 commit f282d9e
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 20 deletions.
19 changes: 17 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,23 @@ The challenges assume three files (replace 00 with the day of the challenge).
- `Sources/Day00.swift`: the code to solve the challenge
- `Tests/Day00.swift`: any unit tests that you want to include

To start a new day's challenge, make a copy of these files and update as
necessary. The `AdventOfCode.swift` file controls which day's challenge is run
To start a new day's challenge, make a copy of these files, updating 00 to the
day number.

```diff
// Add each new day implementation to this array:
let allChallenges: [any AdventDay] = [
- Day00()
+ Day00(),
+ Day01(),
]
```

Then implement part 1 and 2. The `AdventOfCode.swift` file controls which challenge
is run with `swift run`. Add your new type to its `allChallenges` array. By default
it runs the most recent challenge.

The `AdventOfCode.swift` file controls which day's challenge is run
with `swift run`. By default that runs the most recent challenge in the package.

To supply command line arguments use `swift run AdventOfCode`. For example,
Expand Down
19 changes: 13 additions & 6 deletions Sources/AdventDay.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import Foundation

protocol AdventDay {
associatedtype Answer = Int

/// The day of the Advent of Code challenge.
///
/// You can implement this property, or, if your type is named with the
Expand All @@ -13,10 +15,15 @@ protocol AdventDay {
init(data: String)

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

/// Computes and returns the answer for part two.
func part2() async throws -> Any
func part2() async throws -> Answer
}

struct PartUnimplemented: Error {
let day: Int
let part: Int
}

extension AdventDay {
Expand All @@ -42,8 +49,8 @@ extension AdventDay {

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

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

guard let dataURL,
let data = try? String(contentsOf: dataURL)
let data = try? String(contentsOf: dataURL, encoding: .utf8)
else {
fatalError("Couldn't find file '\(dataFilename).txt' in the 'Data' directory.")
}

// On Windows, line separators may be CRLF. Converting to LF so that \n
// On Windows, line separators may be CRLF. Converting to LF so that \n
// works for string parsing.
return data.replacingOccurrences(of: "\r", with: "")
}
Expand Down
37 changes: 25 additions & 12 deletions Sources/AdventOfCode.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ struct AdventOfCode: AsyncParsableCommand {
@Flag(help: "Benchmark the time taken by the solution")
var benchmark: Bool = false

@Flag(help: "Run all the days available")
var all: Bool = false

/// The selected day, or the latest day if no selection is provided.
var selectedChallenge: any AdventDay {
get throws {
Expand All @@ -33,36 +36,46 @@ struct AdventOfCode: AsyncParsableCommand {
allChallenges.max(by: { $0.day < $1.day })!
}

func run(part: () async throws -> Any, named: String) async -> Duration {
var result: Result<Any, Error> = .success("<unsolved>")
func run<T>(part: () async throws -> T, named: String) async -> Duration {
var result: Result<T, Error>?
let timing = await ContinuousClock().measure {
do {
result = .success(try await part())
} catch {
result = .failure(error)
}
}
switch result {
switch result! {
case .success(let success):
print("\(named): \(success)")
case .failure(let failure as PartUnimplemented):
print("Day \(failure.day) part \(failure.part) unimplemented")
case .failure(let failure):
print("\(named): Failed with error: \(failure)")
}
return timing
}

func run() async throws {
let challenge = try selectedChallenge
print("Executing Advent of Code challenge \(challenge.day)...")
let challenges =
if all {
allChallenges
} else {
try [selectedChallenge]
}

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

if benchmark {
print("Part 1 took \(timing1), part 2 took \(timing2).")
#if DEBUG
print("Looks like you're benchmarking debug code. Try swift run -c release")
#endif
let timing1 = await run(part: challenge.part1, named: "Part 1")
let timing2 = await run(part: challenge.part2, named: "Part 2")

if benchmark {
print("Part 1 took \(timing1), part 2 took \(timing2).")
#if DEBUG
print("Looks like you're benchmarking debug code. Try swift run -c release")
#endif
}
}
}
}

0 comments on commit f282d9e

Please sign in to comment.