clicoder 0.0.1
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 +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
|
+
[](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>
|