Skip to content

Commit

Permalink
Introduce commands to create and run examples (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
egiurleo committed Dec 3, 2022
1 parent 338f5f1 commit fbb638a
Show file tree
Hide file tree
Showing 7 changed files with 217 additions and 0 deletions.
55 changes: 55 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,61 @@ Done!

This command expects files to be in the format provided by the `scaffold` command. Once again, I would love to make this configurable but haven't gotten around to it yet.

### Examples

It is often helpful to run our solutions against example input. The `example` command can help you create and run examples of your own invention.

#### Create a new example

You can create a new file for example input by running the following command:

```
bundle exec aoc_cli example new 1 A
```

The first argument specifies the day, and the second argument is the name of the example. You may choose whatever name you'd like.

This will generate the following output:

```
Creating examples/01/A.txt...
Creating examples/01/A_expected.yml...
Done!
```

- `examples/01/A.txt` is a blank text file where you can enter your own input for the problem.
- `examples/01/A_expected.yml` is a YAML file with the following content:

```
part_one: ~
part_two: ~
```

Replace the two tildes (`~`) with the expected result of running your solution against the example input provided.

#### Running examples

You can check your solution against an example with the following command:

```
bundle exec aoc_cli example solve 1 A
```

This will output the following:

```
Reading input...
Loading solution...
Running part one with example A...
Part one result: 1034 ✅
Took 0.000259 seconds to solve
Running part two with example A...
Part two result: 7934 ✅
Took 0.000253 seconds to solve
```

## Contributing

Issues and code contributions are welcome! Happy Advent of Code to all who celebrate! 🎁
5 changes: 5 additions & 0 deletions lib/advent_of_code_cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@

module AdventOfCode
class Error < StandardError; end
class ExampleAlreadyExistsError < Error; end
class InvalidDayError < Error; end
class MissingCookieError < Error; end
class MissingExampleError < Error; end
class MissingInputError < Error; end
class MissingSolutionError < Error; end

Expand Down Expand Up @@ -41,6 +43,9 @@ def solve(day)
say "Error: Cannot find solution file.", :red
end

desc "example", "create and run example files"
subcommand "example", Commands::Example::CLI

private

def rescue_invalid_day_error
Expand Down
1 change: 1 addition & 0 deletions lib/advent_of_code_cli/commands.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@

require_relative "commands/command"
require_relative "commands/download"
require_relative "commands/example"
require_relative "commands/scaffold"
require_relative "commands/solve"
8 changes: 8 additions & 0 deletions lib/advent_of_code_cli/commands/command.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ def input_file_name
"inputs/#{day_string}.txt"
end

def example_file_name
"examples/#{day_string}/#{@name}.txt"
end

def example_expected_file_name
"examples/#{day_string}/#{@name}_expected.yml"
end

def create_file(file_name, contents = nil)
File.open(file_name, "w") do |file|
file.puts contents if contents
Expand Down
38 changes: 38 additions & 0 deletions lib/advent_of_code_cli/commands/example.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# frozen_string_literal: true

require_relative "example/new"
require_relative "example/solve"

module AdventOfCode
module Commands
module Example
class CLI < Thor
desc "new DAY NAME", "creates an example file with name NAME for day DAY"
def new(day, name)
New.new(day: day.to_i, name: name).execute
rescue ExampleAlreadyExistsError => e
say(e.message, :red)
rescue InvalidDayError
rescue_invalid_day_error
end

desc "solve DAY NAME", "runs the example with name NAME for day DAY"
def solve(day, name)
Solve.new(day: day.to_i, name: name).execute
rescue InvalidDayError
rescue_invalid_day_error
rescue MissingExampleError
say "Error: Cannot find example file.", :red
rescue AdventOfCode::MissingSolutionError
say "Error: Cannot find solution file.", :red
end

private

def rescue_invalid_day_error
say "Error: The day argument must be an integer between 1 and 25.", :red
end
end
end
end
end
48 changes: 48 additions & 0 deletions lib/advent_of_code_cli/commands/example/new.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# frozen_string_literal: true

module AdventOfCode
module Commands
module Example
class New < Command
def initialize(day:, name:)
@name = name
super(day: day)
end

def execute
unless Dir.exist?("examples")
say("Creating examples directory...")
Dir.mkdir("examples")
end

unless Dir.exist?("examples/#{day_string}")
say("Creating examples/#{day_string} directory...")
Dir.mkdir("examples/#{day_string}")
end

if File.exist?(example_file_name)
raise ExampleAlreadyExistsError,
"could not create example file because file #{example_file_name} already exists"
end

say("Creating #{example_file_name}...")
create_file(example_file_name)

say("Creating #{example_expected_file_name}...")
create_file(example_expected_file_name, expected_file_contents)

say "Done!", :green
end

private

def expected_file_contents
<<~RUBY
part_one: ~
part_two: ~
RUBY
end
end
end
end
end
62 changes: 62 additions & 0 deletions lib/advent_of_code_cli/commands/example/solve.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# frozen_string_literal: true

require "yaml"

module AdventOfCode
module Commands
module Example
class Solve < Command
def initialize(day:, name:)
@name = name
super(day: day)
end

def execute
raise MissingExampleError unless File.exist?(example_file_name)
raise MissingExampleError unless File.exist?(example_expected_file_name)
raise MissingSolutionError unless File.exist?(solution_file_name)

say "Reading input..."
input = File.readlines(example_file_name, chomp: true)

say "Loading solution..."
load(solution_file_name)

module_name = "Day#{day_string}"

say "\nRunning part one with example #{@name}..."
solution(module_name, "one", input)

say "\nRunning part two with example #{@name}..."
solution(module_name, "two", input)

say "\nDone!", :green
end

private

def solution(module_name, part, input)
start_time = Time.now
result = Object.const_get(module_name).send("part_#{part}", input)
end_time = Time.now

expected_result = expected_answers["part_#{part}"]

if expected_result.nil?
say "Part #{part} result: #{result} ⚠️ (no expectation provided)"
elsif result == expected_result
say "Part #{part} result: #{result} ✅"
else
say "Part #{part} result: #{result} ❌ (expected #{expected_result})"
end

say "Took #{end_time - start_time} seconds to solve"
end

def expected_answers
@expected_answers ||= YAML.safe_load(File.read(example_expected_file_name))
end
end
end
end
end

0 comments on commit fbb638a

Please sign in to comment.