Tools for online programming contests.
| | Target | contest
field | Scrape samples | Download system tests | Submit |
| :---------------------- | :------------------------------------------- | :---------------------: | :-------------: | :-------------------: | :-------------: |
| AtCoder | atcoder.jp/contests/{}
| .*
| ✓ | ✓ | ✓ |
| Codeforces | codeforces.com/contest/{}
| unsigned 64-bit integer | ✓ | N/A | ✓ |
| yukicoder (Problems) | yukicoder.me/problems/no/{}
| no
| ✓ | ✓ | ✓ |
| yukicoder (Contests) | yukicoder.me/contests/{}
| (?!no)
| ✓ | ✓ | ✓ |
https://github.com/qryxip/snowchains/releases
cargo install
(Crates.io)
$ cargo install snowchains
cargo install
(GitHub)
$ cargo install --git https://github.com/qryxip/snowchains
``` snowchains 0.3.0 Ryo Yamashita qryxip@gmail.com Tools for online programming contests
USAGE:
snowchains [FLAGS] [OPTIONS] [directory]
snowchains [FLAGS] [OPTIONS] FLAGS:
-h, --help Prints help information
-V, --version Prints version information SUBCOMMANDS:
init Creates a config file ("snowchains.toml") [aliases: i]
switch Modifies values in a config file [aliases: w, checkout, c]
login Logges in to a service [aliases: l]
participate Participates in a contest [aliases: p]
download An alias for ```toml
target = ".snowchains/target.json" [console]
cjk = false [shell]
bash = ["/usr/bin/bash", "-c", "${command}"] ruby = { runner = "/usr/bin/ruby", extension = "rb" } [testfiles]
path = ".snowchains/tests/${service}/${snakecase(contest)}/${snakecase(problem)}.${extension}" [session]
timeout = "60s"
silent = false
robots = true
cookies = "~/.local/share/snowchains/cookies.json"
apitokens = "~/.local/share/snowchains/apitokens/${service}.json"
dropbox = false [session.retry]
retries = 2
method = ["get"] [session.retrieve]
extension = "yml"
textfiledir = "${service}/${snakecase(contest)}/tests/${snakecase(problem)}" [judge]
testfile_extensions = ["json", "toml", "yaml", "yml"] display_limit = "1KiB" [env."t"]
CXXFLAGS = "-std=gnu++17 -g -fsanitize=undefined -DGLIBCXXDEBUG -Wall -Wextra"
RUSTVERSION = "stable"
RUSTOPT_LEVEL = "0" [env."(equal mode 'release)"]
CXXFLAGS = "-std=gnu++17 -O2 -Wall -Wextra"
RUSTOPTLEVEL = "2" [env."(equal '(service mode) '('atcoder 'debug))"]
CXXFLAGS = "-std=gnu++1y -I/usr/include/boost -g -fsanitize=undefined -DGLIBCXXDEBUG -Wall -Wextra" [env."(equal '(service mode) '('atcoder 'release))"]
CXXFLAGS = "-std=gnu++1y -I/usr/include/boost -O2 -Wall -Wextra"
RUST_VERSION = "1.15.1" [env."(equal '(service mode) '('codeforces 'debug))"]
CXXFLAGS = "-std=gnu++17 -g -fsanitize=undefined -DGLIBCXXDEBUG -Wall -Wextra" [env."(equal '(service mode) '('codeforces 'release))"]
CXXFLAGS = "-std=gnu++17 -O2 -Wall -Wextra"
RUST_VERSION = "1.31.1" [env."(equal '(service mode) '('yukicoder 'debug))"]
CXXFLAGS = "-std=gnu++14 -lm -g -fsanitize=undefined -DGLIBCXXDEBUG -Wall -Wextra" [env."(equal '(service mode) '('yukicoder 'release))"]
CXXFLAGS = "-std=gnu++1z -lm -O2 -Wall -Wextra"
RUST_VERSION = "1.30.1" [[hooks.retrieve.testcases]]
ruby = '''
require 'fileutils'
require 'json' def check_system(args)
unless system(*args) then raise ' result = JSON.load(STDIN) openbrowser = result['commandlinearguments']['open']
outputjson = result['commandlinearguments']['json'] ||
result['commandlinearguments']['output'] == 'json' if openbrowser && !outputjson then
service = result['target']['service']
contest = result['target']['contestsnakecase'] unless Dir.exists? './%s/%s/rs' % [service, contest] then
FileUtils.mkdirp './%s/%s' % [service, contest]
checksystem ['cargo', 'new', '--vcs', 'none', '--lib', '--edition', '2015',
'--name', '%s_%s' % [service, contest],
'%s/%s/rs' % [service, contest]]
Dir.mkdir './%s/%s/rs/src/bin' % [service, contest]
FileUtils.rm './%s/%s/rs/src/lib.rs' % [service, contest]
end args = ['emacsclient', '-n'] result['problems'].each do |problem|
src = './%s/%s/rs/src/bin/%s.rs' %
[service, contest, problem['slugsnakecase']]
unless File.exist? src then
FileUtils.cp './templates/rs/src/bin/%s.rs' % [service], src
end
args << src << problem['test_suite']['location']
end check_system args
end
''' [tester]
src = "testers/py/${kebabcase(problem)}.py"
run = { bash = './venv/bin/python3 "$SNOWCHAINSSRC" $SNOWCHAINSARGSJOINED' }
working_directory = "testers/py" [languages.'c++']
src = "${service}/${snakecase(contest)}/cpp/${kebabcase(problem)}.cpp"
bin = "${service}/${snakecase(contest)}/cpp/build/${kebabcase(problem)}"
compile = { bash = 'g++ $CXXFLAGS -o "$SNOWCHAINSBIN" "$SNOWCHAINSSRC"' }
run = ["${bin}"]
workingdirectory = "${service}/${snakecase(contest)}/cpp" [languages.'c++'.names]
atcoder = "C++14 (GCC 5.4.1)"
codeforces = "GNU G++17 7.3.0"
yukicoder = "C++17(1z) (gcc 8.2.0)" [languages.rust]
src = "${service}/${snakecase(contest)}/rs/src/bin/${kebabcase(problem)}.rs"
bin = "${service}/${snakecase(contest)}/rs/target/manually/${mode}/${kebabcase(problem)}"
compile = ["rustc", "+${env:RUSTVERSION}", "-C", "opt-level=${env:RUSTOPTLEVEL}", "-o", "${bin}", "${src}"]
run = ["${bin}"]
workingdirectory = "${service}/${snake_case(contest)}/rs" [languages.rust.names]
atcoder = "Rust (1.15.1)"
codeforces = "Rust 1.31.1"
yukicoder = "Rust (1.30.1)" [languages.go]
src = "${service}/${snakecase(contest)}/go/${kebabcase(problem)}.go"
bin = "${service}/${snakecase(contest)}/go/${kebabcase(problem)}"
compile = ["go", "build", "-o", "${bin}", "${src}"]
run = ["${bin}"]
workingdirectory = "${service}/${snakecase(contest)}/go" [languages.go.names]
atcoder = "Go (1.6)"
codeforces = "Go 1.11.4"
yukicoder = "Go (1.11.2)" [languages.haskell]
src = "${service}/${snakecase(contest)}/hs/app/${pascalcase(problem)}.hs"
bin = "${service}/${snakecase(contest)}/hs/target/${pascalcase(problem)}"
compile = ["stack", "ghc", "--", "-O2", "-o", "${bin}", "${src}"]
run = ["${bin}"]
workingdirectory = "${service}/${snakecase(contest)}/hs" [languages.haskell.names]
atcoder = "Haskell (GHC 7.10.3)"
codeforces = "Haskell GHC 8.6.3"
yukicoder = "Haskell (8.6.2)" [languages.bash]
src = "${service}/${snakecase(contest)}/bash/${kebabcase(problem)}.bash"
run = ["bash", "${src}"]
workingdirectory = "${service}/${snakecase(contest)}/bash" [languages.bash.names]
atcoder = "Bash (GNU bash v4.3.11)"
yukicoder = "Bash (Bash 4.2.46)" [languages.python3]
src = "${service}/${snakecase(contest)}/py/${kebabcase(problem)}.py"
run = ["../../../venvs/python3${service}/bin/python3", "${src}"]
workingdirectory = "${service}/${snake_case(contest)}/py" [languages.python3.names]
atcoder = "Python3 (3.4.3)"
codeforces = "Python 3.7.2"
yukicoder = "Python3 (3.7.1 + numpy 1.14.5 + scipy 1.1.0)" [languages.pypy3]
src = "${service}/${snakecase(contest)}/py/${kebabcase(problem)}.py"
run = ["../../../venvs/pypy3${service}/bin/python3", "${src}"]
workingdirectory = "${service}/${snake_case(contest)}/py" [languages.pypy3.names]
atcoder = "PyPy3 (2.4.0)"
codeforces = "PyPy 3.5 (6.0.0)"
yukicoder = "PyPy3 (6.0.0)" [languages.java]
src = "${service}/${snakecase(contest)}/java/src/main/java/${pascalcase(problem)}.java"
transpiled = "${service}/${snakecase(contest)}/java/build/replaced/${lowercase(pascalcase(problem))}/src/Main.java"
bin = "${service}/${snakecase(contest)}/java/build/replaced/${lowercase(pascalcase(problem))}/classes/Main.class"
transpile = { bash = 'cat "$SNOWCHAINSSRC" | sed -r "s/class\s+$SNOWCHAINSPROBLEMPASCALCASE/class Main/g" > "$SNOWCHAINSTRANSPILED"' }
compile = ["javac", "-d", "./build/replaced/${lowercase(pascalcase(problem))}/classes", "${transpiled}"]
run = ["java", "-classpath", "./build/replaced/${lowercase(pascalcase(problem))}/classes", "Main"]
workingdirectory = "${service}/${snake_case(contest)}/java" [languages.java.names]
atcoder = "Java8 (OpenJDK 1.8.0)"
codeforces = "Java 1.8.0_162"
yukicoder = "Java8 (openjdk 1.8.0.191)" [languages.scala]
src = "${service}/${snakecase(contest)}/scala/src/main/scala/${pascalcase(problem)}.scala"
transpiled = "${service}/${snakecase(contest)}/scala/target/replaced/${lowercase(pascalcase(problem))}/src/Main.scala"
bin = "${service}/${snakecase(contest)}/scala/target/replaced/${lowercase(pascalcase(problem))}/classes/Main.class"
transpile = { bash = 'cat "$SNOWCHAINSSRC" | sed -r "s/object\s+$SNOWCHAINSPROBLEMPASCALCASE/object Main/g" > "$SNOWCHAINSTRANSPILED"' }
compile = ["scalac", "-optimise", "-d", "./target/replaced/${lowercase(pascalcase(problem))}/classes", "${transpiled}"]
run = ["scala", "-classpath", "./target/replaced/${lowercase(pascalcase(problem))}/classes", "Main"]
workingdirectory = "${service}/${snake_case(contest)}/scala" [languages.scala.names]
atcoder = "Scala (2.11.7)"
codeforces = "Scala 2.12.8"
yukicoder = "Scala(Beta) (2.12.7)" [languages.'c#']
src = "${service}/${snakecase(contest)}/cs/${pascalcase(problem)}/${pascalcase(problem)}.cs"
bin = "${service}/${snakecase(contest)}/cs/${pascalcase(problem)}/bin/Release/${pascalcase(problem)}.exe"
compile = ["mcs", "-o+", "-r:System.Numerics", "-out:${bin}", "${src}"]
run = ["mono", "${bin}"]
workingdirectory = "${service}/${snakecase(contest)}/cs" [languages.'c#'.names]
atcoder = "C# (Mono 4.6.2.0)"
codeforces = "C# Mono 5.18"
yukicoder = "C#(mono) (mono 5.16.0.187)" # [languages.text]
src = "${service}/${snakecase(contest)}/txt/${snakecase(problem)}.txt"
run = ["cat", "${src}"]
workingdirectory = "${service}/${snakecase(contest)}/txt" [languages.text.names]
atcoder = "Text (cat)"
yukicoder = "Text (cat 8.22)"
``` https://atcoder.jp/contests/practice/tasks/practice_1 type: batch # "batch", "interactive", or "unsubmittable"
timelimit: 2000ms # optional
match: exact # "any", "exact", or "float" cases:
- name: Sample 1
in: |
1
2 3
test
out: |
6 test
- name: Sample 2
in: |
72
128 256
myonmyon
out: |
456 myonmyon
# "name" and "out" are optional
- in: |
1000
1000 1000
oooooooooooooo
``` ```toml
type = 'batch'
timelimit = '2000ms'
match = 'exact' [[cases]]
name = 'Sample 1'
in = '''
1
2 3
test
'''
out = '''
6 test
''' [[cases]]
name = 'Sample 2'
in = '''
72
128 256
myonmyon
'''
out = '''
456 myonmyon
''' [[cases]]
in = '''
1000
1000 1000
oooooooooooooo
'''
``` https://atcoder.jp/contests/tricky/tasks/tricky_2 type: batch
timelimit: 2000ms
match:
float:
absoluteerror: 1e-9
relativeerror: 1e-9 cases:
- name "Sample 1"
in: |
3
1 -3 2
-10 30 -20
100 -300 200
out: |
2 1.000 2.000
2 1.000 2.000
2 1.000 2.000
``` ```toml
type = 'batch'
timelimit = '2000ms' [match.float]
absoluteerror = 1e-9
relativeerror = 1e-9 [[cases]]
name = 'Sample 1'
in = '''
3
1 -3 2
-10 30 -20
100 -300 200
'''
out = '''
2 1.000 2.000
2 1.000 2.000
2 1.000 2.000
'''
``` https://atcoder.jp/contests/practice/tasks/practice_2 type: interactive
timelimit: 2000ms each_args:
- [ABCDE]
- [EDCBA]
- [ABCDEFGHIJKLMNOPQRSTUVWXYZ]
- [ZYXWVUTSRQPONMLKJIHGFEDCBA]
``` ```python
import re
import sys def main() -> None:
bs = sys.argv[1]
n = len(bs)
q = 7 if n == 5 else 100 if name == 'main':
main()
``` ```haskell
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
module Main (main) where import RIO
import qualified RIO.ByteString as B
import RIO.List
import RIO.List.Partial
import System.Environment
import System.Exit
import System.IO
import Text.Printf main :: IO ()
main = do
RIO.hSetBuffering stdout LineBuffering
bs <- (!! 0) <$> getArgs
let n = length bs
q = if n == 5 then 7 else 100 :: Int
reply c1 c2 = B.putStr (if weight c1 < weight c2 then "<\n" else ">\n")
judge a | a == bs = exitSuccess
| otherwise = die "wrong"
weight c = fromMaybe (error "out of bounds") (c Dual-licensed under MIT or Apache-2.0. [FLAGS] [OPTIONS]
snowchains [FLAGS] [OPTIONS] retrieve testcases
[aliases: d]
retrieve Retrieves data [aliases: r]
judge Tests a binary or script [aliases: j, test, t]
submit Submits a source file [aliases: s]
help Prints this message or the help of the given subcommand(s)
```console
$ snowchains init ./
$ snowchains switch --service atcoder --contest practice --language c++
$ # snowchains login atcoder
$ # snowchains participate atcoder practice
$ snowchains retrieve testcases --open # it does not ask your username and password unless they are needed
$ $EDITOR ./.snowchains/tests/atcoder/practice/a.yml # add more test cases
$ $EDITOR ./atcoder/practice/cpp/a.cpp
$ # snowchains judge a
$ snowchains submit a --open # it executes `judge` command before submitting
Examples
Config File (snowchains.toml)
alt_width = 100
bash = ["C:/tools/msys64/usr/bin/bash.exe", "-c", "PATH=/usr/bin:$$PATH; ${command}"]
bash = ["C:/msys64/usr/bin/bash.exe", "-c", "PATH=/usr/bin:$$PATH; ${command}"]
bash = ["C:/Program Files/Git/usr/bin/bash.exe", "-c", "PATH=/usr/bin:$$PATH; ${command}"]
ps = ["C:/Windows/System32/WindowsPowerShell/v1.0/powershell.exe", "-Command", "${command}"]
cmd = ["C:/Windows/System32/cmd.exe", "/C", "${command}"]
dropbox = { auth: "~/.local/share/snowchains/dropbox.json" }
jobs = 4
%s
failed' % args[0]; end
end[tester]
src = "testers/hs/app/${pascal_case(problem)}.hs"
bin = "testers/hs/target/${pascal_case(problem)}"
run = { bash = '"$SNOWCHAINSBIN" "$SNOWCHAINSSRC" $SNOWCHAINSARGSJOINED' }
working_directory = "testers/hs"
[languages.'c#']
src = "${service}/${snakecase(contest)}/cs/${pascalcase(problem)}/${pascal_case(problem)}.cs"
bin = "${service}/${snakecase(contest)}/cs/${pascalcase(problem)}/bin/Release/${pascal_case(problem)}.exe"
compile = ["csc", "/o+", "/r:System.Numerics", "/out:${bin}", "${src}"]
run = ["${bin}"]
crlftolf: true
workingdirectory = "${service}/${snakecase(contest)}/cs"
[languages.'c#'.names]
atcoder = "C# (Mono 4.6.2.0)"
codeforces = "C# Mono 5.18"
yukicoder = "C# (csc 2.8.2.62916)"
json
{
"service": "atcoder",
"contest": "arc100",
"language": "c++"
}
Test file
Batch (one input, one output)
```yaml
```yaml
Interactive
```yaml
def reply(c1, c2):
print('<' if bs.index(c1) < bs.index(c2) else '>', flush=True)
def judge(a):
if a == bs:
sys.exit(0)
else:
print('wrong', file=sys.stderr)
sys.exit(1)
print(f'{n} {q}', flush=True)
for _ in range(q):
ts = re.split(r'[ \n]', sys.stdin.readline())
if len(ts) == 4 and ts[0] == '?':
reply(ts[1], ts[2])
elif len(ts) == 3 and ts[0] == '!':
judge(ts[1])
else:
raise RuntimeError('invalid')
else:
ts = re.split(r'[ \n]', sys.stdin.readline())
if len(ts) == 3 and ts[0] == '!':
judge(ts[1])
raise RuntimeError('answer me')
elemIndex
bs)
printf "%d %d\n" n q
forM_ [1 .. q] $ _ -> words <$> getLine >>= \case
["?", [c1], [c2]] -> reply c1 c2
["!", a] -> judge a
_ -> error "invalid"
```License