at_coder_friends 0.6.3 → 0.6.8
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.
- checksums.yaml +5 -5
- data/.rubocop.yml +3 -2
- data/.travis.yml +1 -3
- data/CHANGELOG.md +37 -0
- data/Gemfile.lock +53 -48
- data/README.md +1 -1
- data/at_coder_friends.gemspec +5 -7
- data/config/default.yml +146 -72
- data/docs/CONFIGURATION.md +222 -136
- data/lib/at_coder_friends.rb +1 -0
- data/lib/at_coder_friends/cli.rb +8 -0
- data/lib/at_coder_friends/config_loader.rb +11 -3
- data/lib/at_coder_friends/context.rb +10 -6
- data/lib/at_coder_friends/emitter.rb +2 -2
- data/lib/at_coder_friends/generator/base.rb +3 -2
- data/lib/at_coder_friends/generator/main.rb +2 -1
- data/lib/at_coder_friends/parser/constraints.rb +1 -0
- data/lib/at_coder_friends/parser/input_format.rb +2 -0
- data/lib/at_coder_friends/parser/input_type.rb +3 -2
- data/lib/at_coder_friends/parser/modulo.rb +1 -1
- data/lib/at_coder_friends/parser/sections.rb +3 -3
- data/lib/at_coder_friends/path_info.rb +51 -0
- data/lib/at_coder_friends/path_util.rb +0 -31
- data/lib/at_coder_friends/scraping/agent.rb +11 -2
- data/lib/at_coder_friends/scraping/custom_test.rb +5 -6
- data/lib/at_coder_friends/scraping/submission.rb +6 -7
- data/lib/at_coder_friends/scraping/tasks.rb +8 -3
- data/lib/at_coder_friends/test_runner/base.rb +17 -4
- data/lib/at_coder_friends/test_runner/judge.rb +7 -9
- data/lib/at_coder_friends/test_runner/sample.rb +2 -4
- data/lib/at_coder_friends/verifier.rb +2 -3
- data/lib/at_coder_friends/version.rb +1 -1
- data/templates/cxx_builtin.cxx.erb +26 -35
- data/templates/ruby_builtin.rb.erb +17 -18
- metadata +7 -16
- data/tasks/regression/check_const.rake +0 -137
- data/tasks/regression/check_diff.rake +0 -30
- data/tasks/regression/check_fmt.rake +0 -45
- data/tasks/regression/check_parse.rake +0 -69
- data/tasks/regression/list_handler.rb +0 -46
- data/tasks/regression/regression.rb +0 -38
- data/tasks/regression/report_handler.rb +0 -20
- data/tasks/regression/section_list.rake +0 -53
- data/tasks/regression/setup.rake +0 -48
@@ -1,45 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'regression'
|
4
|
-
|
5
|
-
module AtCoderFriends
|
6
|
-
# tasks for regression
|
7
|
-
module Regression
|
8
|
-
module_function
|
9
|
-
|
10
|
-
def check_fmt
|
11
|
-
open_report('check_fmt.txt') do |f|
|
12
|
-
local_pbm_list.each do |contest, q, url|
|
13
|
-
next unless (res = process_fmt(contest, q, url))
|
14
|
-
|
15
|
-
f.puts [
|
16
|
-
contest, q, *res.map { |s| tsv_escape(s) }
|
17
|
-
].join("\t")
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def process_fmt(contest, q, url)
|
23
|
-
pbm = local_scraping_agent(nil, contest).fetch_problem(q, url)
|
24
|
-
Parser::Sections.process(pbm)
|
25
|
-
Parser::SampleData.process(pbm)
|
26
|
-
fmt1 = Parser::InputFormat.find_fmt(pbm)
|
27
|
-
return unless fmt1 && !fmt1.empty?
|
28
|
-
|
29
|
-
fmt2 = Parser::InputFormat.normalize_fmt(fmt1).join("\n")
|
30
|
-
Parser::InputFormat.process(pbm)
|
31
|
-
Parser::InputType.process(pbm)
|
32
|
-
inpdefs = pbm.formats_src
|
33
|
-
fmt3 = inpdefs.map(&:to_s).join("\n")
|
34
|
-
fmt4 = inpdefs.any? { |inpdef| inpdef.cols.empty? } ? '○' : ''
|
35
|
-
[fmt1, fmt2, fmt3, fmt4]
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
namespace :regression do
|
41
|
-
desc 'checks input format patterns'
|
42
|
-
task :check_fmt do
|
43
|
-
AtCoderFriends::Regression.check_fmt
|
44
|
-
end
|
45
|
-
end
|
@@ -1,69 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'regression'
|
4
|
-
require 'at_coder_friends'
|
5
|
-
|
6
|
-
module AtCoderFriends
|
7
|
-
# tasks for regression
|
8
|
-
module Regression
|
9
|
-
module_function
|
10
|
-
|
11
|
-
def check_parse
|
12
|
-
list = local_pbm_list.map do |contest, q, url|
|
13
|
-
pbm = local_scraping_agent(nil, contest).fetch_problem(q, url)
|
14
|
-
Parser::Main.process(pbm)
|
15
|
-
flags = [
|
16
|
-
!fmt?(pbm),
|
17
|
-
pbm.samples.all? { |smp| smp.txt.empty? },
|
18
|
-
pbm.options.interactive
|
19
|
-
]
|
20
|
-
[contest, q, flags]
|
21
|
-
end
|
22
|
-
report(list, 'check_parse.txt')
|
23
|
-
end
|
24
|
-
|
25
|
-
def check_bin
|
26
|
-
list = local_pbm_list.map do |contest, q, url|
|
27
|
-
pbm = local_scraping_agent(nil, contest).fetch_problem(q, url)
|
28
|
-
Parser::Main.process(pbm)
|
29
|
-
flags = [pbm.options.binary_values]
|
30
|
-
[contest, q, flags]
|
31
|
-
end
|
32
|
-
report(list, 'check_bin.txt')
|
33
|
-
end
|
34
|
-
|
35
|
-
def fmt?(pbm)
|
36
|
-
fmt = Parser::InputFormat.find_fmt(pbm)
|
37
|
-
fmt && !fmt.empty?
|
38
|
-
end
|
39
|
-
|
40
|
-
def report(list, file)
|
41
|
-
open_report(file) do |f|
|
42
|
-
list
|
43
|
-
.select { |_, _, flags| flags.any? }
|
44
|
-
.map { |c, q, flags| [c, q, flags.map { |flg| f_to_s(flg) }] }
|
45
|
-
.each { |args| f.puts args.flatten.join("\t") }
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def f_to_s(f)
|
50
|
-
if f.is_a?(Array)
|
51
|
-
f
|
52
|
-
else
|
53
|
-
f ? '◯' : '-'
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
namespace :regression do
|
60
|
-
desc 'checks page parse result'
|
61
|
-
task :check_parse do
|
62
|
-
AtCoderFriends::Regression.check_parse
|
63
|
-
end
|
64
|
-
|
65
|
-
desc 'checks binary problem parse result'
|
66
|
-
task :check_bin do
|
67
|
-
AtCoderFriends::Regression.check_bin
|
68
|
-
end
|
69
|
-
end
|
@@ -1,46 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'net/http'
|
4
|
-
require 'uri'
|
5
|
-
require 'json'
|
6
|
-
require 'csv'
|
7
|
-
|
8
|
-
module AtCoderFriends
|
9
|
-
# tasks for regression
|
10
|
-
module Regression
|
11
|
-
module_function
|
12
|
-
|
13
|
-
CONTEST_LIST_URL = 'https://kenkoooo.com/atcoder/resources/contests.json'
|
14
|
-
ACF_HOME = File.expand_path(File.join(__dir__, '..', '..'))
|
15
|
-
REGRESSION_HOME = File.join(ACF_HOME, 'regression')
|
16
|
-
PAGES_DIR = File.join(REGRESSION_HOME, 'pages')
|
17
|
-
EMIT_ORG_DIR = File.join(REGRESSION_HOME, 'emit_org')
|
18
|
-
EMIT_DIR_FMT = File.join(REGRESSION_HOME, 'emit_%<now>s')
|
19
|
-
|
20
|
-
def contest_id_list
|
21
|
-
uri = URI.parse(CONTEST_LIST_URL)
|
22
|
-
json = Net::HTTP.get(uri)
|
23
|
-
contests = JSON.parse(json)
|
24
|
-
puts "Total #{contests.size} contests"
|
25
|
-
contests.map { |h| h['id'] }
|
26
|
-
end
|
27
|
-
|
28
|
-
def local_pbm_list
|
29
|
-
Dir.glob(PAGES_DIR + '/**/*.html').map do |pbm_path|
|
30
|
-
contest = File.basename(File.dirname(pbm_path))
|
31
|
-
q = File.basename(pbm_path, '.html')
|
32
|
-
url = "file://#{pbm_path}"
|
33
|
-
[contest, q, url]
|
34
|
-
end.sort # .take(10)
|
35
|
-
end
|
36
|
-
|
37
|
-
def pbm_list_from_file(file)
|
38
|
-
dat = File.join(REGRESSION_HOME, file)
|
39
|
-
CSV.read(dat, col_sep: "\t", headers: false).map do |contest, q|
|
40
|
-
pbm_path = File.join(PAGES_DIR, contest, "#{q}.html")
|
41
|
-
url = "file://#{pbm_path}"
|
42
|
-
[contest, q, url]
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'mechanize'
|
4
|
-
require 'at_coder_friends'
|
5
|
-
require_relative 'list_handler'
|
6
|
-
require_relative 'report_handler'
|
7
|
-
|
8
|
-
module AtCoderFriends
|
9
|
-
# tasks for regression
|
10
|
-
module Regression
|
11
|
-
module_function
|
12
|
-
|
13
|
-
def scraping_agent(root, contest)
|
14
|
-
root ||= REGRESSION_HOME
|
15
|
-
@ctx = Context.new({}, File.join(root, contest))
|
16
|
-
@ctx.scraping_agent
|
17
|
-
end
|
18
|
-
|
19
|
-
def local_scraping_agent(root, contest)
|
20
|
-
scraping_agent(root, contest)
|
21
|
-
.tap { |sa| sa.agent.pre_connect_hooks.clear }
|
22
|
-
end
|
23
|
-
|
24
|
-
def agent
|
25
|
-
@agent ||= Mechanize.new
|
26
|
-
end
|
27
|
-
|
28
|
-
def pipeline(pbm)
|
29
|
-
Parser::Main.process(pbm)
|
30
|
-
@ctx.generator.process(pbm)
|
31
|
-
@ctx.emitter.emit(pbm)
|
32
|
-
end
|
33
|
-
|
34
|
-
def rmdir_force(dir)
|
35
|
-
FileUtils.rm_r(dir) if Dir.exist?(dir)
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module AtCoderFriends
|
4
|
-
# tasks for regression
|
5
|
-
module Regression
|
6
|
-
module_function
|
7
|
-
|
8
|
-
def open_report(file)
|
9
|
-
File.open(report_path(file), 'wb') { |f| yield f }
|
10
|
-
end
|
11
|
-
|
12
|
-
def report_path(file)
|
13
|
-
File.join(REGRESSION_HOME, file)
|
14
|
-
end
|
15
|
-
|
16
|
-
def tsv_escape(str)
|
17
|
-
'"' + str.gsub('"', '""').gsub("\t", ' ') + '"'
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
@@ -1,53 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'regression'
|
4
|
-
|
5
|
-
module AtCoderFriends
|
6
|
-
# tasks for regression
|
7
|
-
module Regression
|
8
|
-
module_function
|
9
|
-
|
10
|
-
SectionInfo = Struct.new(:contest, :q, :title)
|
11
|
-
|
12
|
-
def section_list
|
13
|
-
list = load_section_list
|
14
|
-
save_section_list(list)
|
15
|
-
end
|
16
|
-
|
17
|
-
def load_section_list
|
18
|
-
local_pbm_list.flat_map do |contest, q, url|
|
19
|
-
page = agent.get(url)
|
20
|
-
%w[h2 h3].flat_map do |tag|
|
21
|
-
page.search(tag).map do |h|
|
22
|
-
SectionInfo.new(contest, q, normalize(h.content))
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def save_section_list(list)
|
29
|
-
open_report('section_list.txt') do |f|
|
30
|
-
list.group_by(&:title).each do |k, vs|
|
31
|
-
f.puts [k, vs.size, vs[0].contest, vs[0].q].join("\t")
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def normalize(s)
|
37
|
-
s
|
38
|
-
.tr('0-9A-Za-z', '0-9A-Za-z')
|
39
|
-
.gsub(/[[:space:]]/, '')
|
40
|
-
.gsub(/[^一-龠_ぁ-ん_ァ-ヶーa-zA-Z0-9 ]/, '')
|
41
|
-
.downcase
|
42
|
-
.gsub(/\d+/, '{N}')
|
43
|
-
.strip
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
namespace :regression do
|
49
|
-
desc 'list all section titles'
|
50
|
-
task :section_list do
|
51
|
-
AtCoderFriends::Regression.section_list
|
52
|
-
end
|
53
|
-
end
|
data/tasks/regression/setup.rake
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'regression'
|
4
|
-
|
5
|
-
module AtCoderFriends
|
6
|
-
# tasks for regression
|
7
|
-
module Regression
|
8
|
-
module_function
|
9
|
-
|
10
|
-
def setup
|
11
|
-
rmdir_force(PAGES_DIR)
|
12
|
-
rmdir_force(EMIT_ORG_DIR)
|
13
|
-
contest_id_list.each do |contest|
|
14
|
-
setup_by_contest(contest)
|
15
|
-
sleep 3
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
def setup_by_contest(contest)
|
20
|
-
scraping_agent(EMIT_ORG_DIR, contest).fetch_all do |pbm|
|
21
|
-
setup_by_pbm(contest, pbm)
|
22
|
-
end
|
23
|
-
rescue StandardError => e
|
24
|
-
p e
|
25
|
-
end
|
26
|
-
|
27
|
-
def setup_by_pbm(contest, pbm)
|
28
|
-
html_path = File.join(PAGES_DIR, contest, "#{pbm.q}.html")
|
29
|
-
save_file(html_path, pbm.page.body)
|
30
|
-
pipeline(pbm)
|
31
|
-
rescue StandardError => e
|
32
|
-
p e
|
33
|
-
end
|
34
|
-
|
35
|
-
def save_file(path, content)
|
36
|
-
dir = File.dirname(path)
|
37
|
-
FileUtils.makedirs(dir) unless Dir.exist?(dir)
|
38
|
-
File.binwrite(path, content)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
namespace :regression do
|
44
|
-
desc 'setup regression environment'
|
45
|
-
task :setup do
|
46
|
-
AtCoderFriends::Regression.setup
|
47
|
-
end
|
48
|
-
end
|