Tools for online programming contests.
| | Target | "contest" attribute | Scrape samples | Download system tests | Submit |
| :---------------------- | :------------------------------------------- | :------------------ | :-------------: | :-------------------: | :-------------: |
| AtCoder | atcoder.jp/contests/{}
| .*
| ✓ | ✓ | ✓ |
| yukicoder (Problems) | yukicoder.me/problems/no/{}
| no
| ✓ | ✓ | ✓ |
| yukicoder (Contests) | yukicoder.me/contests/{}
| (?!no)
| ✓ | ✓ | ✓ |
https://github.com/qryxip/snowchains/releases
Install Cargo with
rustup,
add ~/.cargo/bin
to your $PATH
, and
console
$ cargo install snowchains
To update:
console
$ cargo uninstall snowchains && cargo install snowchains
Or
$ cargo install cargo-update
$ cargo install-update snowchains
``` snowchains 0.1.0 Ryo Yamashita qryxip@gmail.com Tools for online programming contests
USAGE:
snowchains [OPTIONS] [directory]
snowchains [OPTIONS] FLAGS:
-h, --help Prints help information
-V, --version Prints version information SUBCOMMANDS:
init Creates a config file ("snowchains.toml")
switch Modifies values in a config file
login Logges in to a service
participate Participates in a contest
download Downloads test cases
restore Downloads source files you have submitted
judge Tests a binary or script
submit Submits a source file
show Prints information
modify Modifies values in a config file or test files
help Prints this message or the help of the given subcommand(s)
``` ```toml
service = "atcoder"
contest = "arc100"
language = "c++" [console]
cjk = false [shell]
bash = ["/usr/bin/bash", "-c", "${command}"] [testfiles]
path = "${service}/${snakecase(contest)}/tests/${snakecase(problem)}.${extension}" [session]
timeout = "60s"
silent = false
cookies = "~/.local/share/snowchains/${service}"
dropbox = false [session.download]
extension = "yml"
textfiledir = "${service}/${snakecase(contest)}/tests/${snakecase(problem)}" [judge]
testfile_extensions = ["json", "toml", "yaml", "yml"] display_limit = "1KiB" [env.atcoder]
CXXFLAGS = "-std=gnu++1y -I/usr/include/boost -g -fsanitize=undefined -DGLIBCXXDEBUG -Wall -Wextra"
RUST_VERSION = "1.15.1" [env.yukicoder]
CXXFLAGS = "-std=gnu++14 -lm -g -fsanitize=undefined -DGLIBCXXDEBUG -Wall -Wextra"
RUST_VERSION = "1.30.1" [env.other]
CXXFLAGS = "-std=gnu++17 -g -fsanitize=undefined -DGLIBCXXDEBUG -Wall -Wextra"
RUST_VERSION = "stable" [[hooks.switch]]
bash = '''
service="$(echo "$SNOWCHAINSRESULT" | jq -r .new.service)"
contest="$(echo "$SNOWCHAINSRESULT" | jq -r .new.contestsnakecase)"
if [ ! -d "./$service/$contest/rs" ]; then
mkdir -p "./$service/$contest" &&
cargo new --lib --edition 2015 --name "$contest" "./$service/$contest/rs" &&
mkdir "./$service/$contest/rs/src/bin" &&
rm "./$service/$contest/rs/src/lib.rs"
fi
''' [[hooks.download]]
bash = '''
if [ "$(echo "$SNOWCHAINSRESULT" | jq -r .openinbrowser)" = true ]; then
service="$(echo "$SNOWCHAINSRESULT" | jq -r .service)"
echo "$SNOWCHAINSRESULT" |
jq -r '
. as $root
| .problems
| map("./" + $root.service + "/" + $root.contest.sluglowercase + "/rs/src/bin/" + .namekebabcase + ".rs")
| join("\n")
' | xargs -d \n -I % -r cp "./templates/rs/src/bin/$service.rs" % &&
echo "$SNOWCHAINSRESULT" |
jq -r '
. as $root
| .problems
| map(["./" + $root.service + "/" + $root.contest.sluglowercase + "/rs/src/bin/" + .namekebabcase + ".rs", .testsuitepath])
| flatten
| join("\n")
' | xargs -d \n -r emacsclient -n
fi
''' [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"
language_ids = { atcoder = "3003", yukicoder = "cpp14" } [languages.rust]
src = "${service}/${snakecase(contest)}/rs/src/bin/${kebabcase(problem)}.rs"
bin = "${service}/${snakecase(contest)}/rs/target/manually/${kebabcase(problem)}"
compile = ["rustc", "+${env:RUSTVERSION}", "-o", "${bin}", "${src}"]
run = ["${bin}"]
workingdirectory = "${service}/${snakecase(contest)}/rs"
languageids = { atcoder = "3504", yukicoder = "rust" } [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"
language_ids = { atcoder = "3013", yukicoder = "go" } [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"
language_ids = { atcoder = "3014", yukicoder = "haskell" } [languages.bash]
src = "${service}/${snakecase(contest)}/bash/${kebabcase(problem)}.bash"
run = ["bash", "${src}"]
workingdirectory = "${service}/${snakecase(contest)}/bash"
language_ids = { atcoder = "3001", yukicoder = "sh" } [languages.python3]
src = "${service}/${snakecase(contest)}/py/${kebabcase(problem)}.py"
run = ["../../../venvs/python3${service}/bin/python3", "${src}"]
workingdirectory = "${service}/${snakecase(contest)}/py"
languageids = { atcoder = "3023", yukicoder = "python3" } [languages.pypy3]
src = "${service}/${snakecase(contest)}/py/${kebabcase(problem)}.py"
run = ["../../../venvs/pypy3${service}/bin/python3", "${src}"]
workingdirectory = "${service}/${snakecase(contest)}/py"
languageids = { atcoder = "3510", yukicoder = "pypy3" } [languages.java]
src = "${service}/${snakecase(contest)}/java/src/main/java/${pascalcase(problem)}.java"
transpiled = "${service}/${snakecase(contest)}/java/build/replaced/${lowercase(problem)}/src/Main.java"
bin = "${service}/${snakecase(contest)}/java/build/replaced/${lowercase(problem)}/classes/Main.class"
transpile = { bash = 'cat "$SNOWCHAINSSRC" | sed -r "s/class\s+$SNOWCHAINSPROBLEMPASCALCASE/class Main/g" > "$SNOWCHAINSTRANSPILED"' }
compile = ["javac", "-d", "./build/replaced/${lowercase(problem)}/classes", "${transpiled}"]
run = ["java", "-classpath", "./build/replaced/${lowercase(problem)}/classes", "Main"]
workingdirectory = "${service}/${snakecase(contest)}/java"
languageids = { atcoder = "3016", yukicoder = "java8" } [languages.scala]
src = "${service}/${snakecase(contest)}/scala/src/main/scala/${pascalcase(problem)}.scala"
transpiled = "${service}/${snakecase(contest)}/scala/target/replaced/${lowercase(problem)}/src/Main.scala"
bin = "${service}/${snakecase(contest)}/scala/target/replaced/${lowercase(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(problem)}/classes", "${transpiled}"]
run = ["scala", "-classpath", "./target/replaced/${lowercase(problem)}/classes", "Main"]
workingdirectory = "${service}/${snakecase(contest)}/scala"
languageids = { atcoder = "3025", yukicoder = "scala" } [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"
languageids = { atcoder = "3006", yukicoder = "csharpmono" } [languages.text]
src = "${service}/${snakecase(contest)}/txt/${snakecase(problem)}.txt"
run = ["cat", "${src}"]
workingdirectory = "${service}/${snakecase(contest)}/txt"
language_ids = { atcoder = "3027", yukicoder = "text" }
``` 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] console
$ snowchains init ./
$ snowchains switch --service atcoder --contest practice --language c++
$ # snowchains login atcoder
$ # snowchains participate atcoder practice
$ snowchains download --open # does not ask your username and password unless they are needed
$ $EDITOR ./snowchains/atcoder/practice/a.yml # add more test cases
$ $EDITOR ./cpp/a.cpp
$ # snowchains judge a
$ snowchains submit a --open # 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
[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"
language_ids = { atcoder = "3006", yukicoder = "csharp" }
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