aoc_rb 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c55b5953ec5f5609a93d35056a675811dd5777a71514b6f3ed90eb64223212a4
4
+ data.tar.gz: 1cc0e2404ad5efb0a38b5f4870785089fbd3b9d9edf86f8134cb793f43b47d2b
5
+ SHA512:
6
+ metadata.gz: f1d9532728421583243011320330aeb9a5279627fd2c05b842ced50b92ff19650cbaf7e31aa336c71d35f80a6f1dc5e02060aafc9dfb6718cde52f519eb845de
7
+ data.tar.gz: d13d273694aa76935fd809ee3c7a777dbb9ba3449b2a18b411b905c06dd7876961b78e539fa33c4906d7ceaf929c8831615f2d818593cf32900553f6c16be3ff
@@ -0,0 +1,13 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+
10
+ # rspec failure tracking
11
+ .rspec_status
12
+ /.idea/
13
+
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --format documentation
2
+ --color
3
+ --require spec_helper
@@ -0,0 +1,6 @@
1
+ ---
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 2.7.0
6
+ before_install: gem install bundler -v 2.1.2
@@ -0,0 +1,74 @@
1
+ # Contributor Covenant Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ In the interest of fostering an open and welcoming environment, we as
6
+ contributors and maintainers pledge to making participation in our project and
7
+ our community a harassment-free experience for everyone, regardless of age, body
8
+ size, disability, ethnicity, gender identity and expression, level of experience,
9
+ nationality, personal appearance, race, religion, or sexual identity and
10
+ orientation.
11
+
12
+ ## Our Standards
13
+
14
+ Examples of behavior that contributes to creating a positive environment
15
+ include:
16
+
17
+ * Using welcoming and inclusive language
18
+ * Being respectful of differing viewpoints and experiences
19
+ * Gracefully accepting constructive criticism
20
+ * Focusing on what is best for the community
21
+ * Showing empathy towards other community members
22
+
23
+ Examples of unacceptable behavior by participants include:
24
+
25
+ * The use of sexualized language or imagery and unwelcome sexual attention or
26
+ advances
27
+ * Trolling, insulting/derogatory comments, and personal or political attacks
28
+ * Public or private harassment
29
+ * Publishing others' private information, such as a physical or electronic
30
+ address, without explicit permission
31
+ * Other conduct which could reasonably be considered inappropriate in a
32
+ professional setting
33
+
34
+ ## Our Responsibilities
35
+
36
+ Project maintainers are responsible for clarifying the standards of acceptable
37
+ behavior and are expected to take appropriate and fair corrective action in
38
+ response to any instances of unacceptable behavior.
39
+
40
+ Project maintainers have the right and responsibility to remove, edit, or
41
+ reject comments, commits, code, wiki edits, issues, and other contributions
42
+ that are not aligned to this Code of Conduct, or to ban temporarily or
43
+ permanently any contributor for other behaviors that they deem inappropriate,
44
+ threatening, offensive, or harmful.
45
+
46
+ ## Scope
47
+
48
+ This Code of Conduct applies both within project spaces and in public spaces
49
+ when an individual is representing the project or its community. Examples of
50
+ representing a project or community include using an official project e-mail
51
+ address, posting via an official social media account, or acting as an appointed
52
+ representative at an online or offline event. Representation of a project may be
53
+ further defined and clarified by project maintainers.
54
+
55
+ ## Enforcement
56
+
57
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
+ reported by contacting the project team at TODO: Write your email address. All
59
+ complaints will be reviewed and investigated and will result in a response that
60
+ is deemed necessary and appropriate to the circumstances. The project team is
61
+ obligated to maintain confidentiality with regard to the reporter of an incident.
62
+ Further details of specific enforcement policies may be posted separately.
63
+
64
+ Project maintainers who do not follow or enforce the Code of Conduct in good
65
+ faith may face temporary or permanent repercussions as determined by other
66
+ members of the project's leadership.
67
+
68
+ ## Attribution
69
+
70
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
+ available at [https://contributor-covenant.org/version/1/4][version]
72
+
73
+ [homepage]: https://contributor-covenant.org
74
+ [version]: https://contributor-covenant.org/version/1/4/
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source "https://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in aoc_rb.gemspec
4
+ gemspec
5
+
6
+ gem "rake", "~> 12.0"
7
+ gem "byebug"
@@ -0,0 +1,62 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ aoc_rb (0.0.0)
5
+ dotenv (~> 2.7.6)
6
+ httparty (~> 0.18.1)
7
+ nokogiri (~> 1.10.10)
8
+ rspec (~> 3.0)
9
+ thor (~> 1.0.1)
10
+
11
+ GEM
12
+ remote: https://rubygems.org/
13
+ specs:
14
+ addressable (2.7.0)
15
+ public_suffix (>= 2.0.2, < 5.0)
16
+ byebug (11.1.3)
17
+ crack (0.4.4)
18
+ diff-lcs (1.4.4)
19
+ dotenv (2.7.6)
20
+ hashdiff (1.0.1)
21
+ httparty (0.18.1)
22
+ mime-types (~> 3.0)
23
+ multi_xml (>= 0.5.2)
24
+ mime-types (3.3.1)
25
+ mime-types-data (~> 3.2015)
26
+ mime-types-data (3.2020.1104)
27
+ mini_portile2 (2.4.0)
28
+ multi_xml (0.6.0)
29
+ nokogiri (1.10.10)
30
+ mini_portile2 (~> 2.4.0)
31
+ public_suffix (4.0.6)
32
+ rake (12.3.3)
33
+ rspec (3.10.0)
34
+ rspec-core (~> 3.10.0)
35
+ rspec-expectations (~> 3.10.0)
36
+ rspec-mocks (~> 3.10.0)
37
+ rspec-core (3.10.0)
38
+ rspec-support (~> 3.10.0)
39
+ rspec-expectations (3.10.0)
40
+ diff-lcs (>= 1.2.0, < 2.0)
41
+ rspec-support (~> 3.10.0)
42
+ rspec-mocks (3.10.0)
43
+ diff-lcs (>= 1.2.0, < 2.0)
44
+ rspec-support (~> 3.10.0)
45
+ rspec-support (3.10.0)
46
+ thor (1.0.1)
47
+ webmock (3.10.0)
48
+ addressable (>= 2.3.6)
49
+ crack (>= 0.3.2)
50
+ hashdiff (>= 0.4.0, < 2.0.0)
51
+
52
+ PLATFORMS
53
+ ruby
54
+
55
+ DEPENDENCIES
56
+ aoc_rb!
57
+ byebug
58
+ rake (~> 12.0)
59
+ webmock (~> 3.10.0)
60
+
61
+ BUNDLED WITH
62
+ 2.1.4
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 Jon Pascoe
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2020 TODO: Write your name
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,44 @@
1
+ # AocRb
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/aoc_rb`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'aoc_rb'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle install
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install aoc_rb
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/aoc_rb. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/aoc_rb/blob/master/CODE_OF_CONDUCT.md).
36
+
37
+
38
+ ## License
39
+
40
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
41
+
42
+ ## Code of Conduct
43
+
44
+ Everyone interacting in the AocRb project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/aoc_rb/blob/master/CODE_OF_CONDUCT.md).
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,35 @@
1
+ require_relative 'lib/aoc_rb/version'
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = "aoc_rb"
5
+ spec.version = AocRb::VERSION
6
+ spec.authors = ["Jon Pascoe"]
7
+ spec.email = ["jon.pascoe@me.com"]
8
+
9
+ spec.summary = %q{A Ruby toolkit for Advent of Code}
10
+ spec.description = %q{Tools for creating a new project for your Advent of Code solutions, built using Ruby.}
11
+ spec.homepage = "https://github.com/pacso/aoc_rb"
12
+ spec.license = "MIT"
13
+ spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
14
+
15
+ spec.metadata["homepage_uri"] = spec.homepage
16
+ spec.metadata["source_code_uri"] = "https://github.com/pacso/aoc_rb"
17
+ spec.metadata["changelog_uri"] = "https://github.com/pacso/aoc_rb/blob/main/CHANGELOG"
18
+
19
+ # Specify which files should be added to the gem when it is released.
20
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
22
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
+ end
24
+ spec.bindir = "exe"
25
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ["lib"]
27
+
28
+ spec.add_dependency "dotenv", "~> 2.7.6"
29
+ spec.add_dependency "httparty", "~> 0.18.1"
30
+ spec.add_dependency "thor", "~> 1.0.1"
31
+ spec.add_dependency "rspec", "~> 3.0"
32
+ spec.add_dependency "nokogiri", "~> 1.10.10"
33
+
34
+ spec.add_development_dependency "webmock", "~> 3.10.0"
35
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "aoc_rb"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/exe/aoc ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "aoc_rb/cli"
@@ -0,0 +1,7 @@
1
+ puts "lib/aoc_rb.rb"
2
+
3
+ Dir[File.join(File.dirname(__FILE__), "aoc_rb", "**", "*.rb")].each { |file| require file }
4
+
5
+ module AocRb
6
+ class Error < StandardError; end
7
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AocRb
4
+ class AocApi
5
+ include HTTParty
6
+ base_uri 'https://adventofcode.com'
7
+
8
+ def initialize(session)
9
+ @options = { headers: { 'Cookie' => "session=#{session}" }, follow_redirects: false }
10
+ end
11
+
12
+ def puzzle_instructions(year, day)
13
+ self.class.get(puzzle_path(year, day), @options)
14
+ end
15
+
16
+ def puzzle_input(year, day)
17
+ self.class.get(input_path(year, day), @options)
18
+ end
19
+
20
+ def submit_answer(year, day, level, answer)
21
+ options_with_answer = @options.merge({ body: { level: level.to_s, answer: answer.to_s } })
22
+ self.class.post(answer_path(year, day), options_with_answer)
23
+ end
24
+
25
+ private
26
+ def puzzle_path(year, day)
27
+ "/#{year}/day/#{day}"
28
+ end
29
+
30
+ def input_path(year, day)
31
+ puzzle_path(year, day) + "/input"
32
+ end
33
+
34
+ def answer_path(year, day)
35
+ puzzle_path(year, day) + "/answer"
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,121 @@
1
+ require "httparty"
2
+ require 'dotenv/load'
3
+ require "thor"
4
+
5
+ require 'aoc_rb/aoc_api'
6
+ require 'aoc_rb/puzzle'
7
+ require 'aoc_rb/puzzle_input'
8
+ require 'aoc_rb/puzzle_solution'
9
+ require 'aoc_rb/puzzle_source'
10
+
11
+ src_files = File.join(Dir.getwd, "challenges", "**", "*.rb")
12
+
13
+ Dir[src_files].each do |file|
14
+ if File.exist? file
15
+ require file
16
+ else
17
+ puts "missing file #{file}"
18
+ end
19
+ end
20
+
21
+ module AocRb
22
+ class App < Thor
23
+ desc "fetch", "Downloads the input file and problem statement for today, or an optionally specified year / day", hide: true
24
+ method_option :year, aliases: "-y", type: :numeric, default: Time.now.year
25
+ method_option :day, aliases: "-d", type: :numeric, default: Time.now.day
26
+
27
+ def fetch(year = options[:year], day = options[:day])
28
+ fetch_input(year, day)
29
+ fetch_instructions(year, day)
30
+ end
31
+
32
+ desc "download", "downloads an input file for today, or an optionally specified year & day", hide: true
33
+ method_option :year, aliases: "-y", type: :numeric, default: Time.now.year
34
+ method_option :day, aliases: "-d", type: :numeric, default: Time.now.day
35
+
36
+ def fetch_input(year = options[:year], day = options[:day])
37
+ AocRb::PuzzleInput.download(year, day)
38
+ end
39
+
40
+ desc "fetch_instructions", "downloads the available instructions for today, or the specified date", hide: true
41
+ method_option :year, aliases: "-y", type: :numeric, default: Time.now.year
42
+ method_option :day, aliases: "-d", type: :numeric, default: Time.now.day
43
+
44
+ def fetch_instructions(year = options[:year], day = options[:day])
45
+ AocRb::Puzzle.fetch_instructions(year, day)
46
+ end
47
+
48
+ desc "bootstrap", "sets up the boilerplate for a new daily challenge", hide: true
49
+ method_option :year, aliases: "-y", type: :numeric, default: Time.now.year
50
+ method_option :day, aliases: "-d", type: :numeric, default: Time.now.day
51
+
52
+ def bootstrap(year = options[:year], day = options[:day])
53
+ AocRb::Puzzle.create_templates(year, day)
54
+ end
55
+
56
+ desc "exec", "executes and optionally submits the puzzle for today, or the specified date"
57
+ long_desc <<~LONGDESC
58
+ `exec` will check if you have instructions for PART 2 of today's puzzle.
59
+ If you don't it will execute part 1, and offer to submit it for you. If
60
+ you do have instructions for part 2, it will execute part 2 and offer to
61
+ submit that.
62
+
63
+ In the event that you've recently submitted an incorrect answer and are
64
+ being throttled, it will detect the remaining time you need to wait, and
65
+ automatically submit your solution 1s after that delay.
66
+
67
+ You can optionally specify another day/year to exec if you're not working on
68
+ today's puzzle.
69
+ LONGDESC
70
+ method_option :year, aliases: "-y", type: :numeric, default: Time.now.year
71
+ method_option :day, aliases: "-d", type: :numeric, default: Time.now.day
72
+
73
+ def exec(year = options[:year], day = options[:day])
74
+ puzzle = AocRb::PuzzleSource.create_puzzle(year, day)
75
+ input = AocRb::PuzzleInput.load(year, day)
76
+
77
+ level = Puzzle.instructions_exist?(year, day, :part_2) ? 2 : 1
78
+ puts "#{year} Day #{day}"
79
+ solution = PuzzleSource.run_part("part #{level}") { puzzle.send("part_#{level}", input) }
80
+
81
+ puts "Submit solution? #{solution} (y/N)"
82
+ submit = STDIN.gets.chomp.downcase
83
+ puts "We said #{submit}"
84
+
85
+ if submit == "y"
86
+ if PuzzleSolution.submit(level, year, day, solution)
87
+ puts "Correct!"
88
+
89
+ if level == 1
90
+ puts "Downloading part 2!"
91
+ fetch_instructions(year, day)
92
+ end
93
+ end
94
+ end
95
+ end
96
+
97
+ desc "output", "outputs results from your solution for the given day"
98
+ method_option :year, aliases: "-y", type: :numeric, default: Time.now.year
99
+ method_option :day, aliases: "-d", type: :numeric, default: Time.now.day
100
+
101
+ def output(year = options[:year], day = options[:day])
102
+ puzzle = AocRb::PuzzleSource.create_puzzle(year, day)
103
+ input = AocRb::PuzzleInput.load(year, day)
104
+
105
+ AocRb::PuzzleSource.run_part('part 1') { puzzle.part_1(input) }
106
+ puts
107
+ AocRb::PuzzleSource.run_part('part 2') { puzzle.part_2(input) }
108
+ end
109
+
110
+ desc "prep", "preps everything you need for a new puzzle"
111
+ method_option :year, aliases: "-y", type: :numeric, default: Time.now.year
112
+ method_option :day, aliases: "-d", type: :numeric, default: Time.now.day
113
+
114
+ def prep(year = options[:year], day = options[:day])
115
+ fetch(year, day)
116
+ bootstrap(year, day)
117
+ end
118
+ end
119
+ end
120
+
121
+ AocRb::App.start
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AocRb
4
+ module AppLoader
5
+ extend self
6
+
7
+ RUBY = Gem.ruby
8
+ EXECUTABLES = ["bin/aoc", "script/aoc"]
9
+
10
+ def exec_app
11
+ original_cwd = Dir.pwd
12
+
13
+ loop do
14
+ if exe = find_executable
15
+ contents = File.read(exe)
16
+
17
+ if /THIS IS THE BIN FILE/.match?(contents)
18
+ exec RUBY, exe, *ARGV
19
+ break
20
+ end
21
+ end
22
+
23
+ Dir.chdir(original_cwd) && return if Pathname.new(Dir.pwd).root?
24
+
25
+ Dir.chdir("..")
26
+ end
27
+ end
28
+
29
+ def find_executable
30
+ EXECUTABLES.find { |exe| File.file?(exe) }
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "aoc_rb/app_loader"
4
+ AocRb::AppLoader.exec_app
5
+
6
+ require "thor"
7
+
8
+ module AocRb
9
+ class Cli < Thor
10
+ desc "new NAME", "Creates a new AoC project with the given name"
11
+
12
+ def new(name)
13
+ project_dir = File.join(Dir.getwd, name)
14
+ if File.exist?(project_dir)
15
+ puts "ERROR: #{project_dir} already exists!"
16
+ exit 0
17
+ end
18
+
19
+ bin_dir = File.join(project_dir, "bin")
20
+ bin_path = File.join(bin_dir, "aoc")
21
+ bin_template = File.join(File.dirname(__FILE__), "../../templates/bin/aoc")
22
+ FileUtils.mkdir_p bin_dir
23
+ File.open(bin_path, "w") { |f| f.write(File.read(bin_template)) }
24
+
25
+ spec_dir = File.join(project_dir, "spec")
26
+ spec_helper_path = File.join(spec_dir, "spec_helper.rb")
27
+ spec_helper_template = File.join(File.dirname(__FILE__), "../../templates/spec/spec_helper.rb")
28
+ FileUtils.mkdir_p spec_dir
29
+ File.open(spec_helper_path, "w") { |f| f.write(File.read(spec_helper_template)) }
30
+
31
+ env_template_path = File.join(project_dir, ".env-template")
32
+ env_template = File.join(File.dirname(__FILE__), "../../templates/.env-template")
33
+ File.open(env_template_path, "w") { |f| f.write(File.read(env_template)) }
34
+
35
+ gemfile_dst = File.join(project_dir, "Gemfile")
36
+ gemfile_src = File.join(File.dirname(__FILE__), "../../templates/Gemfile")
37
+ File.open(gemfile_dst, "w") { |f| f.write(File.read(gemfile_src))}
38
+ end
39
+ end
40
+ end
41
+
42
+ AocRb::Cli.start
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "nokogiri"
4
+ require "erb"
5
+
6
+ module AocRb
7
+ module Puzzle
8
+ extend self
9
+
10
+ def create_templates(year, day)
11
+ create_source(year, day)
12
+ create_spec(year, day)
13
+ end
14
+
15
+ def padded(day)
16
+ day.to_s.rjust(2, "0")
17
+ end
18
+
19
+ def create_source(year, day)
20
+ source_dir = File.join("challenges", year.to_s, padded(day))
21
+ source_path = File.join(source_dir, "solution.rb")
22
+ FileUtils.mkdir_p(source_dir) unless Dir.exist?(source_dir)
23
+ PuzzleInput.skip_if_exists(source_path) do
24
+ template = File.read(File.join(File.dirname(__FILE__), "../../templates", "solution.rb.erb"))
25
+ @year = year.to_s
26
+ @day = padded(day)
27
+ File.open(source_path, "w") do |f|
28
+ f.write ERB.new(template).result(binding)
29
+ end
30
+ end
31
+ end
32
+
33
+ def create_spec(year, day)
34
+ spec_dir = File.join("spec", year.to_s, padded(day))
35
+ spec_path = File.join(spec_dir, "solution_spec.rb")
36
+ FileUtils.mkdir_p(spec_dir) unless Dir.exist?(spec_dir)
37
+ PuzzleInput.skip_if_exists(spec_path) do
38
+ template = File.read(File.join(File.dirname(__FILE__), "../../templates", "solution_spec.rb.erb"))
39
+ @year = year.to_s
40
+ @day = padded(day)
41
+ File.open(spec_path, "w") do |f|
42
+ f.write ERB.new(template).result(binding)
43
+ end
44
+ end
45
+ end
46
+
47
+ def fetch_instructions(year, day)
48
+ create_required_directories year, day
49
+
50
+ api = AocRb::AocApi.new(ENV['AOC_COOKIE'])
51
+ content = api.puzzle_instructions(year, day)
52
+
53
+ parse_and_save_instructions(year, day, content.body)
54
+ end
55
+
56
+ def create_puzzle(year, day)
57
+
58
+ end
59
+
60
+ def instructions_exist?(year, day, part)
61
+ filename = File.join("challenges", year.to_s, padded(day), "#{part}.md")
62
+ File.exist?(filename)
63
+ end
64
+
65
+ def create_required_directories(year, day)
66
+ year_directory = File.join("challenges", year.to_s, padded(day))
67
+ FileUtils.mkdir_p(year_directory) unless Dir.exist?(year_directory)
68
+ end
69
+
70
+ def parse_and_save_instructions(year, day, content)
71
+ doc = Nokogiri::HTML(content)
72
+ articles = doc.css("article.day-desc")
73
+ articles.each_with_index { |article, index| process_article(year, day, article, index) }
74
+ end
75
+
76
+ def process_article(year, day, article, index)
77
+ part_num = index + 1
78
+ filename = File.join("challenges", year.to_s, padded(day), "part_#{part_num}.md")
79
+
80
+ File.open(filename, "w") do |f|
81
+ process_page_content(f, article)
82
+ f.close
83
+ end
84
+ end
85
+
86
+ def process_page_content(f, child, indent_level = 0, strip_em = false)
87
+ # byebug
88
+ return unless child.respond_to?(:name)
89
+ case child.name
90
+ when "text"
91
+ f.write child.content.chomp.gsub("\n", "\n" + (" " * indent_level))
92
+ when "h2"
93
+ f.write "## "
94
+ process_children f, child.children, indent_level, strip_em
95
+ f.write "\n\n"
96
+ when "p"
97
+ process_children f, child.children, indent_level, strip_em
98
+ f.write "\n\n"
99
+ when "ul"
100
+ process_children f, child.children, indent_level, strip_em
101
+ f.write "\n"
102
+ when "li"
103
+ f.write "* "
104
+ process_children f, child.children, indent_level, strip_em
105
+ f.write "\n"
106
+ when "em"
107
+ f.write "**" unless strip_em
108
+ process_children f, child.children
109
+ f.write "**" unless strip_em
110
+ when "code"
111
+ f.write " " * indent_level
112
+ f.write "``" unless indent_level > 0
113
+ process_children f, child.children, indent_level, true
114
+ f.write "``" unless indent_level > 0
115
+ when "pre"
116
+ process_children f, child.children, 4, strip_em
117
+ f.write "\n\n"
118
+ else
119
+ process_children f, child.children, indent_level, strip_em
120
+ # byebug
121
+ end
122
+ end
123
+
124
+ def process_children(f, children, indent_level = 0, strip_em = false)
125
+ children.each do |child|
126
+ process_page_content f, child, indent_level, strip_em
127
+ end
128
+ end
129
+ end
130
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+ module AocRb
3
+ module PuzzleInput
4
+ extend self
5
+
6
+ def load(year, day)
7
+ file_path = puzzle_path(year, day)
8
+ download(year, day) unless File.exist? file_path
9
+ File.read(file_path)
10
+ end
11
+
12
+ def create_required_directories(year, day)
13
+ padded_day = day.to_s.rjust(2, "0")
14
+ year_directory = File.join("challenges", year.to_s, padded_day)
15
+ FileUtils.mkdir_p(year_directory) unless Dir.exist?(year_directory)
16
+ end
17
+
18
+ def puzzle_path(year, day)
19
+ padded_day = day.to_s.rjust(2, "0")
20
+ File.join("challenges", year.to_s, padded_day, "input.txt")
21
+ end
22
+
23
+ def download(year, day)
24
+ aoc_api = AocRb::AocApi.new(ENV['AOC_COOKIE'])
25
+ content = aoc_api.puzzle_input(year, day)
26
+ save_puzzle(year, day, content)
27
+ end
28
+
29
+ def save_puzzle(year, day, content)
30
+ protect_against_early_download(content)
31
+ create_required_directories year, day
32
+ skip_if_exists(puzzle_path(year, day)) do
33
+ File.open(puzzle_path(year, day), "w") { |f| f.write content }
34
+ end
35
+ end
36
+
37
+ def skip_if_exists(file)
38
+ unless File.exist? file
39
+ yield
40
+ else
41
+ puts "#{file} already exists, skipping"
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def protect_against_early_download(content)
48
+ if /the link will be enabled on the calendar the instant this puzzle becomes available/.match?(content)
49
+ puts "ERROR: This resource is not available for download yet"
50
+ exit 0
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AocRb
4
+ module PuzzleSolution
5
+ extend self
6
+
7
+ def submit(level, year, day, answer = nil, allow_waiting = true)
8
+ if answer.nil?
9
+ puzzle = PuzzleSource.create_puzzle(year, day)
10
+ input = PuzzleInput.load(year, day)
11
+ answer = level == 1 ? puzzle.part_1(input) : puzzle.part_2(input)
12
+ end
13
+
14
+ aoc_api = AocApi.new(ENV['AOC_COOKIE'])
15
+ response = aoc_api.submit_answer(year, day, level, answer)
16
+ # puts response.body
17
+
18
+ wrong = /not the right answer/.match?(response.body)
19
+ already_complete = /Did you already complete it/.match?(response.body)
20
+ waiting_regex = /You have (\d*m* *\d+s) left to wait/
21
+ waiting = waiting_regex.match?(response.body)
22
+
23
+ doc = Nokogiri::HTML(response.body)
24
+ articles = doc.css("article")
25
+ puts articles[0].content
26
+
27
+ puts "That's not the right answer" if wrong
28
+ puts "You have already completed this challenge" if already_complete
29
+ if waiting && allow_waiting
30
+ delay = 1
31
+ time = waiting_regex.match(response.body)[1]
32
+ time_parts = time.split(" ")
33
+ time_parts.each do |part|
34
+ if part.match?(/m/)
35
+ delay += part.match(/(\d+)m/)[1].to_i * 60
36
+ elsif part.match?(/s/)
37
+ delay += part.match(/(\d+)s/)[1].to_i
38
+ end
39
+ end
40
+
41
+ puts "WAITING for #{delay} seconds ..."
42
+ sleep delay
43
+ return submit(level, year, day, answer, false)
44
+ end
45
+
46
+ !wrong && !already_complete && !waiting
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+ require "benchmark"
3
+
4
+ module AocRb
5
+ module PuzzleSource
6
+ extend self
7
+
8
+ def create_puzzle(year, day)
9
+ padded_day = Puzzle.padded(day)
10
+ begin
11
+ Module.const_get("Year#{year}").const_get("Day#{padded_day}").new
12
+ rescue NameError
13
+ puts "There is no solution for this puzzle"
14
+ end
15
+ end
16
+
17
+ def run_part(part_name)
18
+ solution = nil
19
+ t = Benchmark.realtime do
20
+ solution = yield
21
+ if !solution.nil?
22
+ puts "Result for #{part_name}:"
23
+ puts solution
24
+ else
25
+ puts "no result for #{part_name}"
26
+ end
27
+ end
28
+ puts "(obtained in #{t} seconds)" unless solution.nil?
29
+
30
+ solution
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,3 @@
1
+ module AocRb
2
+ VERSION = "0.0.0"
3
+ end
@@ -0,0 +1 @@
1
+ AOC_COOKIE=AOC_COOKIE
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
6
+
7
+ gem "aoc_rb"
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'aoc' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ # THIS IS THE BIN FILE
12
+
13
+ require "pathname"
14
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
15
+ Pathname.new(__FILE__).realpath)
16
+
17
+ bundle_binstub = File.expand_path("../bundle", __FILE__)
18
+
19
+ if File.file?(bundle_binstub)
20
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
21
+ load(bundle_binstub)
22
+ else
23
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
24
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
25
+ end
26
+ end
27
+
28
+ $LOAD_PATH.unshift File.expand_path("../..", __FILE__)
29
+
30
+ require "rubygems"
31
+ require "bundler/setup"
32
+
33
+ # load Gem.bin_path("aoc_rb", "aoc")
34
+
35
+ require 'aoc_rb/app'
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ module Year<%= @year %>
3
+ class Day<%= @day %>
4
+ def part_1(input)
5
+ nil
6
+ end
7
+
8
+ def part_2(input)
9
+ nil
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+ require 'spec_helper'
3
+
4
+ RSpec.describe Year<%= @year %>::Day<%= @day %> do
5
+ let(:puzzle) { Year<%= @year %>::Day<%= @day %>.new }
6
+ let(:input) { File.read(File.join(File.dirname(__FILE__), "../../../challenges/<%= @year %>/<%= @day.to_s.rjust(2, "0") %>/input.txt")) }
7
+ let(:example_input) {
8
+ <<~EOF
9
+ change
10
+ me
11
+ EOF
12
+ }
13
+
14
+ describe "part 1" do
15
+ it "returns nil for the example input" do
16
+ expect(puzzle.part_1(example_input)).to eq(nil)
17
+ end
18
+
19
+ it "returns nil for my input" do
20
+ expect(puzzle.part_1(input)).to eq(nil)
21
+ end
22
+ end
23
+
24
+ describe "part 2" do
25
+ it "returns nil for the example input" do
26
+ expect(puzzle.part_2(example_input)).to eq(nil)
27
+ end
28
+
29
+ it "returns nil for my input" do
30
+ expect(puzzle.part_2(input)).to eq(nil)
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ Dir[File.join(File.dirname(__FILE__), "..", "challenges", "**", "*.rb")].each do |file|
4
+ require file
5
+ end
6
+
7
+ RSpec.configure do |config|
8
+
9
+ end
metadata ADDED
@@ -0,0 +1,162 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aoc_rb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jon Pascoe
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-12-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: dotenv
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 2.7.6
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 2.7.6
27
+ - !ruby/object:Gem::Dependency
28
+ name: httparty
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.18.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.18.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: thor
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.0.1
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.0.1
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: nokogiri
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 1.10.10
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 1.10.10
83
+ - !ruby/object:Gem::Dependency
84
+ name: webmock
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: 3.10.0
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 3.10.0
97
+ description: Tools for creating a new project for your Advent of Code solutions, built
98
+ using Ruby.
99
+ email:
100
+ - jon.pascoe@me.com
101
+ executables:
102
+ - aoc
103
+ extensions: []
104
+ extra_rdoc_files: []
105
+ files:
106
+ - ".gitignore"
107
+ - ".rspec"
108
+ - ".travis.yml"
109
+ - CODE_OF_CONDUCT.md
110
+ - Gemfile
111
+ - Gemfile.lock
112
+ - LICENSE
113
+ - LICENSE.txt
114
+ - README.md
115
+ - Rakefile
116
+ - aoc_rb.gemspec
117
+ - bin/console
118
+ - bin/setup
119
+ - exe/aoc
120
+ - lib/aoc_rb.rb
121
+ - lib/aoc_rb/aoc_api.rb
122
+ - lib/aoc_rb/app.rb
123
+ - lib/aoc_rb/app_loader.rb
124
+ - lib/aoc_rb/cli.rb
125
+ - lib/aoc_rb/puzzle.rb
126
+ - lib/aoc_rb/puzzle_input.rb
127
+ - lib/aoc_rb/puzzle_solution.rb
128
+ - lib/aoc_rb/puzzle_source.rb
129
+ - lib/aoc_rb/version.rb
130
+ - templates/.env-template
131
+ - templates/Gemfile
132
+ - templates/bin/aoc
133
+ - templates/solution.rb.erb
134
+ - templates/solution_spec.rb.erb
135
+ - templates/spec/spec_helper.rb
136
+ homepage: https://github.com/pacso/aoc_rb
137
+ licenses:
138
+ - MIT
139
+ metadata:
140
+ homepage_uri: https://github.com/pacso/aoc_rb
141
+ source_code_uri: https://github.com/pacso/aoc_rb
142
+ changelog_uri: https://github.com/pacso/aoc_rb/blob/main/CHANGELOG
143
+ post_install_message:
144
+ rdoc_options: []
145
+ require_paths:
146
+ - lib
147
+ required_ruby_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: 2.3.0
152
+ required_rubygems_version: !ruby/object:Gem::Requirement
153
+ requirements:
154
+ - - ">="
155
+ - !ruby/object:Gem::Version
156
+ version: '0'
157
+ requirements: []
158
+ rubygems_version: 3.1.2
159
+ signing_key:
160
+ specification_version: 4
161
+ summary: A Ruby toolkit for Advent of Code
162
+ test_files: []