clicoder 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +202 -0
- data/Rakefile +1 -0
- data/bin/clicoder +4 -0
- data/clicoder.gemspec +35 -0
- data/features/sample_site.feature +79 -0
- data/features/step_definitions/sample_site_steps.rb +80 -0
- data/features/support/setup.rb +26 -0
- data/features/support/webmock.rb +5 -0
- data/fixtures/clicoder.d/Makefile +6 -0
- data/fixtures/clicoder.d/config.yml +9 -0
- data/fixtures/clicoder.d/template.cpp +6 -0
- data/fixtures/sample_problem.html +23 -0
- data/lib/clicoder/cli.rb +190 -0
- data/lib/clicoder/config.rb +50 -0
- data/lib/clicoder/judge.rb +30 -0
- data/lib/clicoder/site_base.rb +131 -0
- data/lib/clicoder/sites/aoj.rb +59 -0
- data/lib/clicoder/sites/atcoder.rb +66 -0
- data/lib/clicoder/sites/sample_site.rb +52 -0
- data/lib/clicoder/version.rb +3 -0
- data/lib/clicoder.rb +32 -0
- data/spec/config_spec.rb +84 -0
- data/spec/judge_spec.rb +64 -0
- data/spec/site_base_spec.rb +41 -0
- data/spec/sites/aoj_spec.rb +75 -0
- data/spec/sites/sample_site_spec.rb +117 -0
- data/spec/spec_helper.rb +21 -0
- metadata +268 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 191b32a24645db71142a867c9d5663ccc529657e
|
4
|
+
data.tar.gz: 7a2427b24acb997930e00486f68971e8f4fc3616
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1f4a2a6a5910de3a7148dc0399cefa73d62590b006a4acc09a391ebf67e22975521d6c1e91f85ff144d02c87ac289471cf7cb6e8f7fcfa9ba3e791a36d3d4a74
|
7
|
+
data.tar.gz: 7bbd322adfafb27d7c8febffa834aa5f7d5bde772054831b8261e24dc415c19bf47a16b31dc4e56ab91b487ecf790d5719034d83db2fed500a3e498ca2cb06ca
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Genki Sugimoto
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,202 @@
|
|
1
|
+
# CLI Coder
|
2
|
+
|
3
|
+
Command Line Interface for Online Programming Contests.
|
4
|
+
|
5
|
+
# Why
|
6
|
+
|
7
|
+
Programming contests are fun.
|
8
|
+
However, there are chores which are not fun, like
|
9
|
+
|
10
|
+
* copy a sample input and run a program against it
|
11
|
+
* compare it with a sample answer
|
12
|
+
* submit (copy & paste or select a file)
|
13
|
+
|
14
|
+
This tool automate these chores so that we can enjoy only really fun part of programming contests (which is, thinking and implementing).
|
15
|
+
|
16
|
+
# Demo Video
|
17
|
+
|
18
|
+
Here is a demo solving a problem from AOJ (links to youtube):
|
19
|
+
|
20
|
+
[![CLI Coder demo](http://img.youtube.com/vi/sVH5EIOxDf8/0.jpg)](http://www.youtube.com/watch?v=sVH5EIOxDf8)
|
21
|
+
|
22
|
+
# Installation
|
23
|
+
|
24
|
+
$ gem install clicoder
|
25
|
+
|
26
|
+
# Preparation
|
27
|
+
|
28
|
+
## ~/.clicoder.d/config.yml (required)
|
29
|
+
|
30
|
+
It contains
|
31
|
+
|
32
|
+
* configurations of template files
|
33
|
+
* configurations for various programming contest sites.
|
34
|
+
|
35
|
+
Example:
|
36
|
+
|
37
|
+
```yaml
|
38
|
+
---
|
39
|
+
default:
|
40
|
+
template: template.cpp # template file. relative to this file
|
41
|
+
makefile: Makefile # Makefile used by CLI Coder. relative to this file
|
42
|
+
aoj:
|
43
|
+
template: aoj_template.cpp # template only used for site 'aoj'
|
44
|
+
user_id: Glen_S
|
45
|
+
password: PASSWORD
|
46
|
+
atcoder:
|
47
|
+
user_id: Glen_S
|
48
|
+
password: PASSWORD
|
49
|
+
```
|
50
|
+
|
51
|
+
## template.cpp etc. (recommended)
|
52
|
+
|
53
|
+
It is recommended to put your template file under `~/.clicoder.d`.
|
54
|
+
It will be copied into working directories as `main.*` each time you start solving new problems.
|
55
|
+
|
56
|
+
If you don't use templates, make sure you write your solutions in files named `main.*`.
|
57
|
+
|
58
|
+
## Makefile (required)
|
59
|
+
|
60
|
+
It is recommended to put your `Makefile` under `~/.clicoder.d`.
|
61
|
+
|
62
|
+
CLI Coder uses Makefile to build and execute your program.
|
63
|
+
|
64
|
+
## "build" rule
|
65
|
+
|
66
|
+
It should build your program.
|
67
|
+
|
68
|
+
Example:
|
69
|
+
|
70
|
+
build:
|
71
|
+
g++ -g main.cpp -o a.out
|
72
|
+
|
73
|
+
## "execute" rule
|
74
|
+
|
75
|
+
It has to run your program with redirection from "in.txt" and redirection to "out.txt".
|
76
|
+
|
77
|
+
Example:
|
78
|
+
|
79
|
+
execute:
|
80
|
+
./a.out < in.txt > out.txt
|
81
|
+
|
82
|
+
# Usage
|
83
|
+
|
84
|
+
```sh
|
85
|
+
Commands:
|
86
|
+
clicoder add_test # Add new test case
|
87
|
+
clicoder all # build, execute, and judge
|
88
|
+
clicoder browse # Open problem page with the browser
|
89
|
+
clicoder build # Build your program using `make build`
|
90
|
+
clicoder download # Download description, inputs and outputs
|
91
|
+
clicoder execute # Execute your program using `make execute`
|
92
|
+
clicoder help [COMMAND] # Describe available commands or one specific command
|
93
|
+
clicoder judge # Judge your outputs
|
94
|
+
clicoder new <command> # start a new problem
|
95
|
+
clicoder submit # Submit your program
|
96
|
+
```
|
97
|
+
|
98
|
+
## Sites Available
|
99
|
+
|
100
|
+
* [AOJ](http://judge.u-aizu.ac.jp/onlinejudge/)
|
101
|
+
* [AtCoder](http://atcoder.jp/)
|
102
|
+
|
103
|
+
```sh
|
104
|
+
clicoder new aoj PROBLEM_NUMBER # Prepare directory to deal with new problem from AOJ
|
105
|
+
clicoder new atcoder CONTEST_ID PROBLEM_NUMBER # Prepare directory to deal with new problem from AtCoder
|
106
|
+
```
|
107
|
+
|
108
|
+
### AOJ
|
109
|
+
|
110
|
+
PROBLEM_NUMBER is shown in problem URL like this:
|
111
|
+
|
112
|
+
<pre>http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=<strong>0001</strong></pre>
|
113
|
+
|
114
|
+
### AtCoder
|
115
|
+
|
116
|
+
CONTEST_ID is shown in contest URL like this:
|
117
|
+
|
118
|
+
<pre>http://<strong>arc001</strong>.contest.atcoder.jp/</pre>
|
119
|
+
|
120
|
+
PROBLEM_NUMBER is a number starting from 1, or it can be found in problem URL like this:
|
121
|
+
|
122
|
+
<pre>http://arc001.contest.atcoder.jp/tasks/arc001_<strong>1</strong></pre>
|
123
|
+
|
124
|
+
# Tips
|
125
|
+
|
126
|
+
I recommend you to use this shell function for AOJ:
|
127
|
+
|
128
|
+
```sh
|
129
|
+
function aoj() {
|
130
|
+
clicoder new aoj $1
|
131
|
+
dir=$(printf "%04d" $1)
|
132
|
+
cd $dir
|
133
|
+
}
|
134
|
+
```
|
135
|
+
|
136
|
+
# Contributing
|
137
|
+
|
138
|
+
**Please send your pull requests to "develop" branch.**
|
139
|
+
Thank you for your contributions.
|
140
|
+
|
141
|
+
1. Fork it
|
142
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
143
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
144
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
145
|
+
5. Create new Pull Request
|
146
|
+
|
147
|
+
## Add a Site
|
148
|
+
|
149
|
+
It's easy to add new sites.
|
150
|
+
|
151
|
+
1. Implement new site class
|
152
|
+
2. Add it to the factory method (`new_with_config`) in `lib/clicoder/site_base.rb`
|
153
|
+
3. Add new command (`clicoder new new_site ARGS`) in `lib/clicoder/cli.rb`
|
154
|
+
|
155
|
+
Site classes resides in `lib/clicoder/sites` directory.
|
156
|
+
See existing sites for examples.
|
157
|
+
|
158
|
+
Basically, you need to implement these methods:
|
159
|
+
|
160
|
+
### initialize
|
161
|
+
|
162
|
+
Initialize a site instance (i.e. a problem) and set local configurations (like problem id).
|
163
|
+
|
164
|
+
### submit
|
165
|
+
|
166
|
+
Submit your code.
|
167
|
+
|
168
|
+
### open_submission
|
169
|
+
|
170
|
+
Open submission status page. This will be called automatically after successful submissions.
|
171
|
+
|
172
|
+
### login
|
173
|
+
|
174
|
+
Sometimes you need to login to see problems or to submit your solutions.
|
175
|
+
This method has to log in and execute given block under logged-in condition.
|
176
|
+
|
177
|
+
### site_name
|
178
|
+
|
179
|
+
Returns site name.
|
180
|
+
|
181
|
+
### problem_url
|
182
|
+
|
183
|
+
Returns problem url.
|
184
|
+
|
185
|
+
### description_xpath
|
186
|
+
|
187
|
+
Returns xpath which indicates where the problem description is.
|
188
|
+
Used to download problem description.
|
189
|
+
|
190
|
+
### inputs_xpath
|
191
|
+
|
192
|
+
Returns xpath which indicates where the sample inputs are.
|
193
|
+
Used to download sample inputs.
|
194
|
+
|
195
|
+
### outputs_xpath
|
196
|
+
|
197
|
+
Returns xpath which indicates where the sample outputs are.
|
198
|
+
Used to download sample outputs.
|
199
|
+
|
200
|
+
### working_directory
|
201
|
+
|
202
|
+
Returns a directory name it should create with `clicoder new` command.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/clicoder
ADDED
data/clicoder.gemspec
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'clicoder/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "clicoder"
|
8
|
+
spec.version = Clicoder::VERSION
|
9
|
+
spec.authors = ["Genki Sugimoto"]
|
10
|
+
spec.email = ["cfhoyuk.reccos.nelg@gmail.com"]
|
11
|
+
spec.description = %q{Make it easy to deal with online programming contests from the command line}
|
12
|
+
spec.summary = %q{CLI interface to online programming contests}
|
13
|
+
spec.homepage = "https://github.com/Genki-S/clicoder"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "nokogiri"
|
22
|
+
spec.add_dependency "thor"
|
23
|
+
spec.add_dependency "abstract_method"
|
24
|
+
spec.add_dependency "launchy"
|
25
|
+
spec.add_dependency "reverse_markdown"
|
26
|
+
spec.add_dependency "mechanize"
|
27
|
+
|
28
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
29
|
+
spec.add_development_dependency "rake"
|
30
|
+
spec.add_development_dependency "rspec"
|
31
|
+
spec.add_development_dependency "webmock"
|
32
|
+
spec.add_development_dependency "cucumber"
|
33
|
+
spec.add_development_dependency "aruba"
|
34
|
+
spec.add_development_dependency "pry"
|
35
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
Feature: SampleSite
|
2
|
+
In order to make it easy to deal with programming contests
|
3
|
+
As a CLI
|
4
|
+
I want to provide commands to perform common tasks
|
5
|
+
|
6
|
+
Scenario: Start a new problem
|
7
|
+
When I run `clicoder new sample_site`
|
8
|
+
Then the output should contain "created directory working_directory"
|
9
|
+
|
10
|
+
Scenario: Build a program
|
11
|
+
Given in a problem directory
|
12
|
+
When I run `clicoder build`
|
13
|
+
Then an executable should be generated
|
14
|
+
|
15
|
+
Scenario: Run a program
|
16
|
+
Given in a problem directory
|
17
|
+
When I run `clicoder build`
|
18
|
+
And I run `clicoder execute`
|
19
|
+
Then my answer should be output in my outputs directory
|
20
|
+
|
21
|
+
Scenario: Judge without outputs
|
22
|
+
Given in a problem directory
|
23
|
+
When I run `clicoder judge`
|
24
|
+
Then the output should contain "Wrong Answer"
|
25
|
+
|
26
|
+
Scenario: Judge wrong outputs
|
27
|
+
Given in a problem directory
|
28
|
+
When I run `clicoder build`
|
29
|
+
And I run `clicoder execute`
|
30
|
+
Given outputs are wrong
|
31
|
+
When I run `clicoder judge`
|
32
|
+
Then the output should contain "Wrong Answer"
|
33
|
+
|
34
|
+
Scenario: Judge correct outputs
|
35
|
+
Given in a problem directory
|
36
|
+
When I run `clicoder build`
|
37
|
+
And I run `clicoder execute`
|
38
|
+
Given outputs are correct
|
39
|
+
When I run `clicoder judge`
|
40
|
+
Then the output should contain "Correct Answer"
|
41
|
+
|
42
|
+
Scenario: Judge outputs within allowed absolute error
|
43
|
+
Given in a problem directory
|
44
|
+
And the answer differs in second decimal place
|
45
|
+
When I run `clicoder judge`
|
46
|
+
Then the output should contain "Wrong Answer"
|
47
|
+
|
48
|
+
Scenario: Judge outputs within allowed absolute error
|
49
|
+
Given in a problem directory
|
50
|
+
And the answer differs in second decimal place
|
51
|
+
When I run `clicoder judge -d 2`
|
52
|
+
Then the output should contain "Wrong Answer"
|
53
|
+
|
54
|
+
Scenario: Judge outputs within allowed absolute error
|
55
|
+
Given in a problem directory
|
56
|
+
And the answer differs in second decimal place
|
57
|
+
When I run `clicoder judge -d 1`
|
58
|
+
Then the output should contain "Correct Answer"
|
59
|
+
|
60
|
+
Scenario: Submit with user_id and password
|
61
|
+
Given SampleSite submission url is stubbed with webmock
|
62
|
+
And in a problem directory
|
63
|
+
And Launchy.open is stubbed
|
64
|
+
When I run `clicoder submit`
|
65
|
+
Then the output should contain "Submission Succeeded."
|
66
|
+
And the submission status should be opened
|
67
|
+
|
68
|
+
Scenario: Submit without user_id and password
|
69
|
+
Given SampleSite submission url is stubbed with webmock
|
70
|
+
Given in a problem directory
|
71
|
+
Given I don't have user_id and password
|
72
|
+
When I run `clicoder submit`
|
73
|
+
Then the output should contain "Submission Failed."
|
74
|
+
|
75
|
+
Scenario: Open problem page with browser
|
76
|
+
Given in a problem directory
|
77
|
+
And Launchy.open is stubbed
|
78
|
+
When I run `clicoder browse`
|
79
|
+
Then the problem page should be opened
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'clicoder'
|
2
|
+
require 'clicoder/sites/sample_site'
|
3
|
+
require 'launchy'
|
4
|
+
|
5
|
+
Given /^SampleSite submission url is stubbed with webmock/ do
|
6
|
+
stub_request(:post, "http://samplesite.com/submit").
|
7
|
+
with(:body => {"password"=>"sample_password", "user_id"=>"sample_user_id"},
|
8
|
+
:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/x-www-form-urlencoded', 'Host'=>'samplesite.com', 'User-Agent'=>'Ruby'}).
|
9
|
+
to_return(:status => 200, :body => "Success", :headers => {})
|
10
|
+
stub_request(:post, "http://samplesite.com/submit").
|
11
|
+
with(:body => {"password"=>"", "user_id"=>""},
|
12
|
+
:headers => {'Accept'=>'*/*', 'Accept-Encoding'=>'gzip;q=1.0,deflate;q=0.6,identity;q=0.3', 'Content-Type'=>'application/x-www-form-urlencoded', 'Host'=>'samplesite.com', 'User-Agent'=>'Ruby'}).
|
13
|
+
to_return(:status => 200, :body => "Failure", :headers => {})
|
14
|
+
end
|
15
|
+
|
16
|
+
Given /^I don't have user_id and password/ do
|
17
|
+
FileUtils.rm("#{ENV['HOME']}/.clicoder.d/config.yml")
|
18
|
+
end
|
19
|
+
|
20
|
+
Given /^in a problem directory/ do
|
21
|
+
sample_site = Clicoder::SampleSite.new
|
22
|
+
sample_site.start
|
23
|
+
Dir.chdir(sample_site.working_directory)
|
24
|
+
end
|
25
|
+
|
26
|
+
Given /^there is no output/ do
|
27
|
+
FileUtils.rm(Dir.glob("#{Clicoder::MY_OUTPUTS_DIRNAME}/*.txt"))
|
28
|
+
end
|
29
|
+
|
30
|
+
Given /^outputs are wrong/ do
|
31
|
+
# it's wrong already
|
32
|
+
end
|
33
|
+
|
34
|
+
Given /^outputs are correct/ do
|
35
|
+
FileUtils.cp(Dir.glob("#{Clicoder::OUTPUTS_DIRNAME}/*.txt"), Clicoder::MY_OUTPUTS_DIRNAME)
|
36
|
+
end
|
37
|
+
|
38
|
+
Given /^the answer differs in second decimal place/ do
|
39
|
+
FileUtils.rm(Dir.glob("#{Clicoder::OUTPUTS_DIRNAME}/*"))
|
40
|
+
FileUtils.rm(Dir.glob("#{Clicoder::MY_OUTPUTS_DIRNAME}/*"))
|
41
|
+
File.open("#{Clicoder::OUTPUTS_DIRNAME}/0.txt", 'w') do |f|
|
42
|
+
f.write(<<-EOS)
|
43
|
+
0.11 0.11
|
44
|
+
0.13 0.13
|
45
|
+
EOS
|
46
|
+
end
|
47
|
+
File.open("#{Clicoder::MY_OUTPUTS_DIRNAME}/0.txt", 'w') do |f|
|
48
|
+
f.write(<<-EOS)
|
49
|
+
0.12 0.12
|
50
|
+
0.12 0.12
|
51
|
+
EOS
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
Given /^Launchy.open is stubbed/ do
|
56
|
+
@launchy_open_count = 0
|
57
|
+
Launchy.stub(:open) do |url|
|
58
|
+
@launchy_open_count += 1
|
59
|
+
puts "opening #{url}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
Then /^an executable should be generated/ do
|
64
|
+
expect(File.exists?('a.out')).to be_true
|
65
|
+
end
|
66
|
+
|
67
|
+
Then /^my answer should be output in my outputs directory/ do
|
68
|
+
Dir.glob("#{Clicoder::INPUTS_DIRNAME}/*.txt") do |file|
|
69
|
+
basename = File.basename(file)
|
70
|
+
expect(File.exists?("#{Clicoder::MY_OUTPUTS_DIRNAME}/#{basename}")).to be_true
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
Then /^the submission status should be opened/ do
|
75
|
+
@launchy_open_count.should eq(1)
|
76
|
+
end
|
77
|
+
|
78
|
+
Then /^the problem page should be opened/ do
|
79
|
+
@launchy_open_count.should eq(1)
|
80
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'aruba/cucumber'
|
2
|
+
require 'aruba/in_process'
|
3
|
+
require 'cucumber/rspec/doubles'
|
4
|
+
|
5
|
+
require 'clicoder/cli'
|
6
|
+
|
7
|
+
ENV['PATH'] = "#{Dir.pwd}/bin:#{ENV['PATH']}"
|
8
|
+
|
9
|
+
Aruba::InProcess.main_class = Clicoder::ArubaCLI
|
10
|
+
Aruba.process = Aruba::InProcess
|
11
|
+
|
12
|
+
Before do
|
13
|
+
# Don't use tmp/aruba dir
|
14
|
+
@dirs = ['.']
|
15
|
+
end
|
16
|
+
|
17
|
+
Around do |scenario, block|
|
18
|
+
Dir.mktmpdir do |dir|
|
19
|
+
Dir.chdir(dir) do
|
20
|
+
ENV['HOME'] = Dir.pwd
|
21
|
+
FileUtils.cp_r("#{FIXTURE_DIR}/clicoder.d", '.clicoder.d')
|
22
|
+
|
23
|
+
block.call
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<title>Sample Problem</title>
|
6
|
+
</head>
|
7
|
+
<body>
|
8
|
+
<div id="description">
|
9
|
+
<h1>Sample Problem</h1>
|
10
|
+
<p>
|
11
|
+
Double the numbers.
|
12
|
+
</p>
|
13
|
+
</div>
|
14
|
+
<div id="inputs">
|
15
|
+
<pre>10</pre>
|
16
|
+
<pre>0.01</pre>
|
17
|
+
</div>
|
18
|
+
<div id="outputs">
|
19
|
+
<pre>20</pre>
|
20
|
+
<pre>0.02</pre>
|
21
|
+
</div>
|
22
|
+
</body>
|
23
|
+
</html>
|