aoc_cli 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/Gemfile +12 -0
- data/LICENSE.txt +21 -0
- data/README.md +282 -0
- data/Rakefile +3 -0
- data/aoc_cli.gemspec +30 -0
- data/bin/aoc +4 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/lib/aoc_cli.rb +22 -0
- data/lib/aoc_cli/commands.rb +215 -0
- data/lib/aoc_cli/database.rb +159 -0
- data/lib/aoc_cli/day.rb +153 -0
- data/lib/aoc_cli/db/reddit.db +0 -0
- data/lib/aoc_cli/errors.rb +171 -0
- data/lib/aoc_cli/files.rb +93 -0
- data/lib/aoc_cli/help.rb +53 -0
- data/lib/aoc_cli/interface.rb +137 -0
- data/lib/aoc_cli/paths.rb +93 -0
- data/lib/aoc_cli/solve.rb +109 -0
- data/lib/aoc_cli/tables.rb +138 -0
- data/lib/aoc_cli/tools.rb +91 -0
- data/lib/aoc_cli/version.rb +5 -0
- data/lib/aoc_cli/year.rb +98 -0
- metadata +157 -0
@@ -0,0 +1,138 @@
|
|
1
|
+
module AocCli
|
2
|
+
module Tables
|
3
|
+
class Attempts
|
4
|
+
require 'terminal-table'
|
5
|
+
attr_reader :user, :year, :day, :part, :db
|
6
|
+
def initialize(u:Metafile.get(:user),
|
7
|
+
y:Metafile.get(:year),
|
8
|
+
d:Metafile.get(:day),
|
9
|
+
p:Metafile.get(:part))
|
10
|
+
@user = Validate.user(u)
|
11
|
+
@year = Validate.year(y)
|
12
|
+
@day = Validate.day(d)
|
13
|
+
@part = Validate.part(p)
|
14
|
+
@db = Database::Query
|
15
|
+
.new(path:Paths::Database.cfg(user))
|
16
|
+
end
|
17
|
+
def show
|
18
|
+
puts rows.count > 0 ? table :
|
19
|
+
"You have not attempted this puzzle yet!"
|
20
|
+
end
|
21
|
+
private
|
22
|
+
def table
|
23
|
+
tab = Terminal::Table.new(
|
24
|
+
:headings => headings,
|
25
|
+
:rows => rows,
|
26
|
+
:title => title)
|
27
|
+
tab.style = {
|
28
|
+
:border => :unicode,
|
29
|
+
:alignment => :center}
|
30
|
+
tab
|
31
|
+
end
|
32
|
+
def title
|
33
|
+
"#{year} - Day #{day}:#{part}".bold
|
34
|
+
end
|
35
|
+
def headings
|
36
|
+
["Answer", "Time", "Hint"]
|
37
|
+
end
|
38
|
+
def rows
|
39
|
+
@rows ||= attempts
|
40
|
+
.map{|a| [parse_ans(a), parse_time(a), parse_hint(a)]}
|
41
|
+
end
|
42
|
+
def attempts
|
43
|
+
db.select(
|
44
|
+
t:"attempts",
|
45
|
+
cols:"time, answer, high, low, correct",
|
46
|
+
data:{year:year, day:day, part:part})
|
47
|
+
end
|
48
|
+
def parse_ans(attempt)
|
49
|
+
attempt[4] == 1 ?
|
50
|
+
attempt[1].to_s.green :
|
51
|
+
attempt[1].to_s.red
|
52
|
+
end
|
53
|
+
def parse_time(attempt)
|
54
|
+
DateTime
|
55
|
+
.strptime(attempt[0], "%Y-%m-%d %H:%M:%S %Z")
|
56
|
+
.strftime("%H:%M - %d/%m/%y")
|
57
|
+
end
|
58
|
+
def parse_hint(attempt)
|
59
|
+
attempt[2] == 1 ? "low" :
|
60
|
+
attempt[3] == 1 ? "high" : "-"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
module Stats
|
64
|
+
class Year
|
65
|
+
attr_reader :user, :year, :db
|
66
|
+
def initialize(u:Metafile.get(:user),
|
67
|
+
y:Metafile.get(:year))
|
68
|
+
@user = Validate.user(u)
|
69
|
+
@year = Validate.year(y)
|
70
|
+
@db = Database::Query
|
71
|
+
.new(path:Paths::Database.cfg(user))
|
72
|
+
end
|
73
|
+
def print
|
74
|
+
puts rows.count > 0 ? table :
|
75
|
+
"You have not completed any puzzles yet"
|
76
|
+
end
|
77
|
+
def rows
|
78
|
+
@rows ||= stats.map{|s| [s[0], s[1], s[2], s[3]]}
|
79
|
+
end
|
80
|
+
def table
|
81
|
+
tab = Terminal::Table.new(
|
82
|
+
:headings => headings,
|
83
|
+
:rows => rows,
|
84
|
+
:title => title)
|
85
|
+
tab.style = {
|
86
|
+
:border => :unicode,
|
87
|
+
:alignment => :center}
|
88
|
+
tab
|
89
|
+
end
|
90
|
+
def title
|
91
|
+
"Year #{year}"
|
92
|
+
end
|
93
|
+
def headings
|
94
|
+
["Day", "Part", "Attempts", "Time (h:m:s)"]
|
95
|
+
end
|
96
|
+
def stats
|
97
|
+
@stats ||= db.select(
|
98
|
+
t:"stats",
|
99
|
+
cols:"day, part, attempts, elapsed",
|
100
|
+
data:where)
|
101
|
+
end
|
102
|
+
def where
|
103
|
+
{year:"'#{year}'",
|
104
|
+
correct:"'1'"}
|
105
|
+
end
|
106
|
+
end
|
107
|
+
class Day < Year
|
108
|
+
attr_reader :day
|
109
|
+
def initialize(u:Metafile.get(:user),
|
110
|
+
y:Metafile.get(:year),
|
111
|
+
d:Metafile.get(:day))
|
112
|
+
super(u:u, y:y)
|
113
|
+
@day = Validate.day(d)
|
114
|
+
end
|
115
|
+
def rows
|
116
|
+
@rows ||= stats.map{|s| [s[0], s[1], s[2]]}
|
117
|
+
end
|
118
|
+
def title
|
119
|
+
"Year #{year}: Day #{day}"
|
120
|
+
end
|
121
|
+
def headings
|
122
|
+
["Part", "Attempts", "Time (h:m:s)"]
|
123
|
+
end
|
124
|
+
def stats
|
125
|
+
@stats ||= db.select(
|
126
|
+
t:"stats",
|
127
|
+
cols:"part, attempts, elapsed",
|
128
|
+
data:where)
|
129
|
+
end
|
130
|
+
def where
|
131
|
+
{year:"'#{year}'",
|
132
|
+
day:"'#{day}'",
|
133
|
+
correct:"'1'"}
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module AocCli
|
2
|
+
module Tools
|
3
|
+
class Request
|
4
|
+
require 'curb'
|
5
|
+
attr_reader :user, :year, :day, :base, :page
|
6
|
+
def initialize(u:Metafile.get(:user),
|
7
|
+
y:Metafile.get(:year),
|
8
|
+
d:Metafile.get(:day), p:)
|
9
|
+
@user = Validate.user(u)
|
10
|
+
@year = Validate.year(y)
|
11
|
+
@day = d
|
12
|
+
@page = p
|
13
|
+
@base = "https://adventofcode.com/#{year}"
|
14
|
+
end
|
15
|
+
protected
|
16
|
+
def get
|
17
|
+
Curl.get(url){|h| h.headers['Cookie'] = cookie}
|
18
|
+
.body
|
19
|
+
end
|
20
|
+
def post(data:)
|
21
|
+
Curl.post(url, data){|h| h.headers['Cookie'] =
|
22
|
+
cookie}.body
|
23
|
+
end
|
24
|
+
private
|
25
|
+
def cookie
|
26
|
+
Files::Cookie.new(u:user).key
|
27
|
+
end
|
28
|
+
def url
|
29
|
+
case page.to_sym
|
30
|
+
when :Calendar then base
|
31
|
+
when :Stats then base + "/leaderboard/self"
|
32
|
+
when :Puzzle then base + "/day/#{day}"
|
33
|
+
when :Input then base + "/day/#{day}/input"
|
34
|
+
when :Answer then base + "/day/#{day}/answer"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
class Convert < Request
|
39
|
+
require 'pandoc-ruby'
|
40
|
+
attr_accessor :input
|
41
|
+
def array
|
42
|
+
input.split("\n")
|
43
|
+
end
|
44
|
+
def chunk(f:, t:, t_off:0, f_off:0)
|
45
|
+
pt1 = array.index {|l| l =~ /#{f}/} + t_off
|
46
|
+
pt2 = array.rindex{|l| l =~ /#{t}/} + f_off
|
47
|
+
@input = array.slice(pt1, pt2 - pt1).join("\n")
|
48
|
+
self
|
49
|
+
end
|
50
|
+
def raw
|
51
|
+
input.split("\n").join("\n")
|
52
|
+
end
|
53
|
+
def plain
|
54
|
+
PandocRuby.new(input, f: html, t: :plain).convert
|
55
|
+
end
|
56
|
+
def md
|
57
|
+
PandocRuby.new(input, {f: html, t: :gfm},
|
58
|
+
md_head, md_ref).convert
|
59
|
+
end
|
60
|
+
private
|
61
|
+
def html
|
62
|
+
"html-native_divs-native_spans"
|
63
|
+
end
|
64
|
+
def md_head
|
65
|
+
"--markdown-headings=setext"
|
66
|
+
end
|
67
|
+
def md_ref
|
68
|
+
"--reference-links"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
class Get < Convert
|
72
|
+
def initialize(u:Metafile.get(:user),
|
73
|
+
y:Metafile.get(:year),
|
74
|
+
d:nil, p:)
|
75
|
+
@input = Request
|
76
|
+
.new(u:u, y:y, d:d, p:p)
|
77
|
+
.get
|
78
|
+
end
|
79
|
+
end
|
80
|
+
class Post < Convert
|
81
|
+
def initialize(u:Metafile.get(:user),
|
82
|
+
y:Metafile.get(:year),
|
83
|
+
d:Metafile.get(:day),
|
84
|
+
p:"Answer", data:)
|
85
|
+
@input = Request
|
86
|
+
.new(u:u, y:y, d:d, p:p)
|
87
|
+
.post(data:data)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/lib/aoc_cli/year.rb
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
module AocCli
|
2
|
+
module Year
|
3
|
+
def self.refresh
|
4
|
+
puts "- Updating calendar...".blue
|
5
|
+
Year::Init.new.write
|
6
|
+
Year::Stars.new.write.update_meta
|
7
|
+
end
|
8
|
+
class Init
|
9
|
+
attr_reader :user, :year, :paths
|
10
|
+
def initialize(u:Files::Config.new.def_acc,
|
11
|
+
y:Metafile.get(:year), dir:".")
|
12
|
+
@user = Validate.user(u)
|
13
|
+
@year = Validate.year(y)
|
14
|
+
@paths = Paths::Year.new(u:user, y:year)
|
15
|
+
end
|
16
|
+
def write
|
17
|
+
File.write(paths.local(f:"meta"),
|
18
|
+
Metafile.year(u:user, y:year))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
class Stars < Init
|
22
|
+
attr_reader :stats, :cal
|
23
|
+
def initialize(u:Metafile.get(:user),
|
24
|
+
y:Metafile.get(:year))
|
25
|
+
super(u:u, y:y)
|
26
|
+
@stats = Data::Stats.new(u:user, y:year)
|
27
|
+
@cal = Data::Calendar.new(u:user, y:year)
|
28
|
+
.fill(stars:stats.stars)
|
29
|
+
end
|
30
|
+
def write
|
31
|
+
File.write(paths.local(f:"Stars"), file)
|
32
|
+
self
|
33
|
+
end
|
34
|
+
def update_meta
|
35
|
+
Metafile.add(path:paths.local(f:"meta"), hash:{
|
36
|
+
stars:stats.stars.to_json,
|
37
|
+
total:stats.total_stars})
|
38
|
+
end
|
39
|
+
private
|
40
|
+
def file
|
41
|
+
text = <<~file
|
42
|
+
Year #{year}: #{stats.total_stars}/50 *
|
43
|
+
#{"-" * (cal.data[0].to_s.length + 2)}
|
44
|
+
#{cal.data.join("\n")}\n
|
45
|
+
file
|
46
|
+
text += stats.data.join("\n") if stats.total_stars > 0
|
47
|
+
text
|
48
|
+
end
|
49
|
+
end
|
50
|
+
module Data
|
51
|
+
class YearObject < Init
|
52
|
+
attr_reader :data
|
53
|
+
def initialize(u:Metafile.get(:user),
|
54
|
+
y:Metafile.get(:year))
|
55
|
+
super(u:u, y:y)
|
56
|
+
@data = parse(raw: fetch)
|
57
|
+
end
|
58
|
+
private
|
59
|
+
def fetch
|
60
|
+
Tools::Get.new(u:user, y:year, p:page).plain.split("\n")
|
61
|
+
end
|
62
|
+
end
|
63
|
+
class Calendar < YearObject
|
64
|
+
def page
|
65
|
+
:Calendar
|
66
|
+
end
|
67
|
+
def parse(raw:)
|
68
|
+
raw.drop(raw.index{|l| l =~ /\*\*/})
|
69
|
+
.map{|l| l.gsub(/\*\*/, "")}
|
70
|
+
end
|
71
|
+
def fill(stars:)
|
72
|
+
stars.each{|s, n| data.each{|l| l
|
73
|
+
.gsub!(/(^.*)\b#{s}\b.$/, "\\1#{s}\s#{"*" * n}")}}
|
74
|
+
self
|
75
|
+
end
|
76
|
+
end
|
77
|
+
class Stats < YearObject
|
78
|
+
def page
|
79
|
+
:Stats
|
80
|
+
end
|
81
|
+
def parse(raw:)
|
82
|
+
/You haven't collected/.match?(raw.to_s) ?
|
83
|
+
raw : raw.drop(raw.index{|l| l =~ /^.*Part 1/})
|
84
|
+
end
|
85
|
+
def stars
|
86
|
+
data.map{|l| l.scan(/^\s+\d+/)&.first}
|
87
|
+
.reject{|s| s == nil}
|
88
|
+
.map{|s| [s.to_i, data
|
89
|
+
.grep(/^.*#{s}.*(\-.*){3}$/)
|
90
|
+
.count == 0 ? 2 : 1]}.to_h
|
91
|
+
end
|
92
|
+
def total_stars
|
93
|
+
stars.values.reduce(:+).to_i || 0
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
metadata
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: aoc_cli
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Christian Welham
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-02-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: colorize
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: curb
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: json
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pandoc-ruby
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :runtime
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: sqlite3
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: terminal-table
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
description: A command-line interface for the Advent of Code puzzles. Features include
|
98
|
+
downloading puzzles and inputs, solving puzzles and tracking year progress from
|
99
|
+
within the terminal
|
100
|
+
email:
|
101
|
+
- welhamm@gmail.com
|
102
|
+
executables:
|
103
|
+
- aoc
|
104
|
+
extensions: []
|
105
|
+
extra_rdoc_files: []
|
106
|
+
files:
|
107
|
+
- ".gitignore"
|
108
|
+
- Gemfile
|
109
|
+
- LICENSE.txt
|
110
|
+
- README.md
|
111
|
+
- Rakefile
|
112
|
+
- aoc_cli.gemspec
|
113
|
+
- bin/aoc
|
114
|
+
- bin/console
|
115
|
+
- bin/setup
|
116
|
+
- lib/aoc_cli.rb
|
117
|
+
- lib/aoc_cli/commands.rb
|
118
|
+
- lib/aoc_cli/database.rb
|
119
|
+
- lib/aoc_cli/day.rb
|
120
|
+
- lib/aoc_cli/db/reddit.db
|
121
|
+
- lib/aoc_cli/errors.rb
|
122
|
+
- lib/aoc_cli/files.rb
|
123
|
+
- lib/aoc_cli/help.rb
|
124
|
+
- lib/aoc_cli/interface.rb
|
125
|
+
- lib/aoc_cli/paths.rb
|
126
|
+
- lib/aoc_cli/solve.rb
|
127
|
+
- lib/aoc_cli/tables.rb
|
128
|
+
- lib/aoc_cli/tools.rb
|
129
|
+
- lib/aoc_cli/version.rb
|
130
|
+
- lib/aoc_cli/year.rb
|
131
|
+
homepage: https://github.com/apexatoll/aoc-cli
|
132
|
+
licenses:
|
133
|
+
- MIT
|
134
|
+
metadata:
|
135
|
+
homepage_uri: https://github.com/apexatoll/aoc-cli
|
136
|
+
source_code_uri: https://github.com/apexatoll/aoc-cli
|
137
|
+
changelog_uri: https://github.com/apexatoll/aoc-cli/changelog.md
|
138
|
+
post_install_message:
|
139
|
+
rdoc_options: []
|
140
|
+
require_paths:
|
141
|
+
- lib
|
142
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: 2.3.0
|
147
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - ">="
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
requirements: []
|
153
|
+
rubygems_version: 3.2.3
|
154
|
+
signing_key:
|
155
|
+
specification_version: 4
|
156
|
+
summary: A command-line interface for the Advent of Code puzzles
|
157
|
+
test_files: []
|